From 059cd6f83aa22cdec53be2952d3167038795d1f2 Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Wed, 7 Jul 2021 15:21:00 -0700 Subject: [PATCH 01/39] create middleware logging tests (integration tests) --- SeleniumDockerfile | 15 + apps/integration_tests/log_event_schemas.py | 473 ++++++++++++++ apps/integration_tests/logging_tests.py | 614 ++++++++++++++++++ apps/integration_tests/selenium_tests.py | 551 ++++++++++++++++ docker-compose.selenium.yml | 162 +++++ docker-compose.yml | 2 +- docker-compose/bluebutton_server_start.sh | 34 +- docker-compose/readme.md | 22 +- .../run_integration_tests_local_keybase.sh | 1 + .../run_selenium_tests_local_keybase.sh | 220 +++++++ hhs_oauth_server/settings/test_logging.py | 70 ++ runtests.py | 18 +- templates/design_system/authorize_v2.html | 2 +- 13 files changed, 2160 insertions(+), 24 deletions(-) create mode 100755 SeleniumDockerfile create mode 100755 apps/integration_tests/log_event_schemas.py create mode 100755 apps/integration_tests/logging_tests.py create mode 100755 apps/integration_tests/selenium_tests.py create mode 100755 docker-compose.selenium.yml create mode 100755 docker-compose/run_selenium_tests_local_keybase.sh create mode 100755 hhs_oauth_server/settings/test_logging.py diff --git a/SeleniumDockerfile b/SeleniumDockerfile new file mode 100755 index 000000000..759971700 --- /dev/null +++ b/SeleniumDockerfile @@ -0,0 +1,15 @@ +FROM selenium/standalone-chrome-debug + +ENV PYTHONUNBUFFERED 1 +USER root +RUN apt-get update && apt-get install -yq python3.7 python3-pip +RUN pip3 install --upgrade pip +RUN pip3 install selenium +RUN pip3 install psycopg2-binary==2.8.6 +RUN pip3 install pyyaml==5.4.1 +RUN pip3 install Pillow==8.2.0 +RUN mkdir /code +ADD . /code/ +WORKDIR /code +RUN make reqs-install-dev +RUN ln -s /usr/bin/python3 /usr/local/bin/python \ No newline at end of file diff --git a/apps/integration_tests/log_event_schemas.py b/apps/integration_tests/log_event_schemas.py new file mode 100755 index 000000000..2ab982155 --- /dev/null +++ b/apps/integration_tests/log_event_schemas.py @@ -0,0 +1,473 @@ +from rest_framework import status + +''' + Log entry schemas used for integration tests + See the following for information about the JSON Schema vocabulary: https://json-schema.org/ +''' + +LOG_MIDDLEWARE_EVENT_SCHEMA = { + "title": "MiddlewareLogEventSchema", + "type": "object", + "properties": { + "type": {"pattern": "request_response_middleware"}, + "size": {"type": "integer"}, + "start_time": {"type": "number"}, + "end_time": {"type": "number"}, + "elapsed": {"type": "number"}, + "ip_addr": {"type": "string", "format": "ip-address"}, + "request_uuid": {"type": "string", "format": "uuid"}, + "path": {"pattern": ".+"}, + "request_method": {"pattern": "GET"}, + "request_scheme": {"pattern": "http"}, + "response_code": {"type": "integer", "enum": [status.HTTP_200_OK, status.HTTP_301_MOVED_PERMANENTLY]}, + }, + "required": ["type", "size", "start_time", "end_time", "ip_addr", "elapsed", "request_uuid", + "path", "request_method", "request_scheme", "response_code"] +} + +LOG_MIDDLEWARE_POST_EVENT_SCHEMA = { + "title": "MiddlewareLogEventSchema", + "type": "object", + "properties": { + "type": {"pattern": "request_response_middleware"}, + "size": {"type": "integer"}, + "start_time": {"type": "number"}, + "end_time": {"type": "number"}, + "elapsed": {"type": "number"}, + "ip_addr": {"type": "string", "format": "ip-address"}, + "request_uuid": {"type": "string", "format": "uuid"}, + "path": {"pattern": ".+"}, + "request_method": {"pattern": "POST"}, + "request_scheme": {"pattern": "http"}, + "response_code": {"type": "integer", "enum": [status.HTTP_200_OK]}, + }, + "required": ["type", "size", "start_time", "end_time", "ip_addr", "elapsed", "request_uuid", + "path", "request_method", "request_scheme", "response_code"] +} + +LOG_MIDDLEWARE_TESTCLIENT_AUTHLINK_EVENT_SCHEMA = { + "title": "MiddlewareLogEventSchema", + "type": "object", + "properties": { + "type": {"pattern": "request_response_middleware"}, + "size": {"type": "integer"}, + "start_time": {"type": "number"}, + "end_time": {"type": "number"}, + "ip_addr": {"type": "string", "format": "ip-address"}, + "request_uuid": {"type": "string", "format": "uuid"}, + "path": {"pattern": ".+"}, + "request_method": {"pattern": "GET"}, + "request_scheme": {"pattern": "http"}, + "response_code": {"type": "integer", "enum": [status.HTTP_200_OK]}, + }, + "required": ["type", "size", "start_time", "end_time", "ip_addr", "request_uuid", + "path", "request_method", "request_scheme", "response_code"] +} + +LOG_MIDDLEWARE_AUTH_START_EVENT_SCHEMA = { + "title": "MiddlewareLogEventSchema", + "type": "object", + "properties": { + "type": {"pattern": "request_response_middleware"}, + "size": {"type": "integer"}, + "start_time": {"type": "number"}, + "end_time": {"type": "number"}, + "ip_addr": {"type": "string", "format": "ip-address"}, + "request_uuid": {"type": "string", "format": "uuid"}, + "location": {"pattern": ".+"}, + "auth_uuid": {"type": "string"}, + "auth_client_id": {"type": "string"}, + "auth_app_id": {"type": "string"}, + "auth_app_name": {"pattern": "TestApp"}, + "auth_require_demographic_scopes": {"pattern": "^True$"}, + "req_qparam_client_id": {"type": "string"}, + "req_qparam_response_type": {"pattern": "code"}, + "req_app_name": {"pattern": "TestApp"}, + "req_app_id": {"type": "number"}, + "path": {"pattern": "/v1/o/authorize/"}, + "request_method": {"pattern": "GET"}, + "request_scheme": {"pattern": "http"}, + "response_code": {"type": "integer", "enum": [status.HTTP_302_FOUND]}, + }, + "required": ["type", "size", "start_time", "end_time", "ip_addr", "request_uuid", + "location", "auth_uuid", "auth_client_id", "auth_app_id", "auth_app_name", "auth_require_demographic_scopes", + "req_qparam_client_id", "req_qparam_response_type", "req_app_name", "req_app_id", + "path", "request_method", "request_scheme", "response_code"] +} + +LOG_MIDDLEWARE_MEDICARE_LOGIN_EVENT_SCHEMA = { + "title": "MiddlewareLogEventSchema", + "type": "object", + "properties": { + "type": {"pattern": "request_response_middleware"}, + "size": {"type": "integer"}, + "start_time": {"type": "number"}, + "end_time": {"type": "number"}, + "ip_addr": {"type": "string", "format": "ip-address"}, + "request_uuid": {"type": "string", "format": "uuid"}, + "location": {"pattern": ".+"}, + "auth_uuid": {"type": "string"}, + "auth_client_id": {"type": "string"}, + "auth_app_id": {"type": "string"}, + "auth_app_name": {"pattern": "TestApp"}, + "auth_require_demographic_scopes": {"pattern": "^True$"}, + "path": {"pattern": "/mymedicare/login"}, + "request_method": {"pattern": "GET"}, + "request_scheme": {"pattern": "http"}, + "response_code": {"type": "integer", "enum": [status.HTTP_302_FOUND]}, + }, + "required": ["type", "size", "start_time", "end_time", "ip_addr", "request_uuid", + "location", "auth_uuid", "auth_client_id", "auth_app_id", "auth_app_name", "auth_require_demographic_scopes", + "path", "request_method", "request_scheme", "response_code"] +} + +LOG_MIDDLEWARE_MEDICARE_CALLBACK_EVENT_SCHEMA = { + "title": "MiddlewareLogEventSchema", + "type": "object", + "properties": { + "type": {"pattern": "request_response_middleware"}, + "size": {"type": "integer"}, + "start_time": {"type": "number"}, + "end_time": {"type": "number"}, + "ip_addr": {"type": "string", "format": "ip-address"}, + "request_uuid": {"type": "string", "format": "uuid"}, + "location": {"pattern": ".+"}, + "auth_uuid": {"type": "string"}, + "auth_client_id": {"type": "string"}, + "auth_app_id": {"type": "string"}, + "auth_app_name": {"pattern": "TestApp"}, + "auth_crosswalk_action": {"pattern": "R"}, + "auth_require_demographic_scopes": {"pattern": "^True$"}, + "req_user_id": {"type": "number"}, + "req_user_username": {"pattern": "fred"}, + "req_fhir_id": {"type": "string"}, + "path": {"pattern": "/mymedicare/sls-callback"}, + "user": {"type": "string"}, + "fhir_id": {"type": "string"}, + "request_method": {"pattern": "GET"}, + "request_scheme": {"pattern": "http"}, + "response_code": {"type": "integer", "enum": [status.HTTP_302_FOUND]}, + }, + "required": ["type", "size", "start_time", "end_time", "ip_addr", "request_uuid", + "location", "auth_uuid", "auth_client_id", "auth_app_id", "auth_app_name", "auth_require_demographic_scopes", + "req_user_id", "req_user_username", "req_fhir_id", + "path", "user", "fhir_id", "request_method", "request_scheme", "response_code"] +} + +LOG_MIDDLEWARE_AUTHORIZE_EVENT_SCHEMA = { + "title": "MiddlewareLogEventSchema", + "type": "object", + "properties": { + "type": {"pattern": "request_response_middleware"}, + "size": {"type": "integer"}, + "start_time": {"type": "number"}, + "end_time": {"type": "number"}, + "ip_addr": {"type": "string", "format": "ip-address"}, + "request_uuid": {"type": "string", "format": "uuid"}, + "location": {"pattern": ".*"}, + "auth_uuid": {"type": "string"}, + "auth_client_id": {"type": "string"}, + "auth_app_id": {"type": "string"}, + "auth_app_name": {"pattern": "TestApp"}, + "auth_crosswalk_action": {"pattern": "R"}, + "auth_require_demographic_scopes": {"pattern": "^True$"}, + "req_qparam_client_id": {"type": "string"}, + "req_qparam_response_type": {"pattern": "code"}, + "req_app_name": {"pattern": "TestApp"}, + "req_app_id": {"type": "number"}, + "path": {"pattern": "/v1/o/authorize/.+"}, + "user": {"type": "string"}, + "fhir_id": {"type": "string"}, + "request_method": {"pattern": "GET"}, + "request_scheme": {"pattern": "http"}, + "response_code": {"type": "integer", "enum": [status.HTTP_200_OK]}, + }, + "required": ["type", "size", "start_time", "end_time", "ip_addr", "request_uuid", + "location", "auth_uuid", "auth_client_id", "auth_app_id", "auth_app_name", "auth_crosswalk_action", + "auth_require_demographic_scopes", "req_qparam_client_id", + "req_qparam_response_type", "req_app_name", "req_app_id", + "path", "user", "fhir_id", "request_method", "request_scheme", "response_code"] +} + +LOG_MIDDLEWARE_ACCESS_GRANT_EVENT_SCHEMA = { + "title": "MiddlewareLogEventSchema", + "type": "object", + "properties": { + "type": {"pattern": "request_response_middleware"}, + "size": {"type": "integer"}, + "start_time": {"type": "number"}, + "end_time": {"type": "number"}, + "ip_addr": {"type": "string", "format": "ip-address"}, + "request_uuid": {"type": "string", "format": "uuid"}, + "location": {"pattern": ".+"}, + "auth_uuid": {"type": "string"}, + "auth_client_id": {"type": "string"}, + "auth_app_id": {"type": "string"}, + "auth_app_name": {"pattern": "TestApp"}, + "auth_crosswalk_action": {"pattern": "R"}, + "auth_require_demographic_scopes": {"pattern": "^True$"}, + "req_redirect_uri": {"type": "string"}, + "req_scope": {"type": "string"}, + "req_share_demographic_scopes": {"pattern": "^True$"}, + "req_allow": {"pattern": "Allow"}, + "req_user_id": {"type": "integer"}, + "req_user_username": {"pattern": "fred"}, + "req_fhir_id": {"type": "string"}, + "req_qparam_client_id": {"type": "string"}, + "req_qparam_response_type": {"pattern": "code"}, + "req_app_name": {"pattern": "TestApp"}, + "req_app_id": {"type": "number"}, + "path": {"pattern": "/v1/o/authorize/.+"}, + "user": {"type": "string"}, + "fhir_id": {"type": "string"}, + "request_method": {"pattern": "POST"}, + "request_scheme": {"pattern": "http"}, + "response_code": {"type": "integer", "enum": [status.HTTP_302_FOUND]}, + }, + "required": ["type", "size", "start_time", "end_time", "ip_addr", "request_uuid", + "location", "auth_uuid", "auth_client_id", "auth_app_id", "auth_app_name", + "auth_crosswalk_action", "auth_require_demographic_scopes", + "req_redirect_uri", "req_scope", "req_user_username", "req_fhir_id", + "req_qparam_client_id", "req_qparam_response_type", "req_app_name", "req_app_id", + "path", "user", "fhir_id", "request_method", "request_scheme", "response_code"] +} + +LOG_MIDDLEWARE_TESTCLIENT_FHIR_READ_EVENT_SCHEMA = { + "title": "RequestResponseLogSchema", + "type": "object", + "properties": { + "type": {"pattern": "request_response_middleware"}, + "size": {"type": "integer"}, + "start_time": {"type": "number"}, + "end_time": {"type": "number"}, + "ip_addr": {"type": "string", "format": "ip-address"}, + "request_uuid": {"type": "string", "format": "uuid"}, + "request_method": {"pattern": "GET"}, + "request_scheme": {"pattern": "http"}, + "response_code": {"type": "integer", "enum": [status.HTTP_200_OK]}, + "path": {"pattern": "/testclient/.+"}, + }, + "required": ["type", "size", "start_time", "end_time", "ip_addr", "request_uuid", + "request_method", "request_scheme", "response_code", "path"] +} + +LOG_MIDDLEWARE_TESTCLIENT_FHIR_SEARCH_EVENT_SCHEMA = { + "title": "RequestResponseLogSchema", + "type": "object", + "properties": { + "type": {"pattern": "request_response_middleware"}, + "size": {"type": "integer"}, + "start_time": {"type": "number"}, + "end_time": {"type": "number"}, + "ip_addr": {"type": "string", "format": "ip-address"}, + "request_uuid": {"type": "string", "format": "uuid"}, + "request_method": {"pattern": "GET"}, + "request_scheme": {"pattern": "http"}, + "response_code": {"type": "integer", "enum": [status.HTTP_200_OK]}, + "path": {"pattern": "/testclient/.+"}, + }, + "required": ["type", "size", "start_time", "end_time", "ip_addr", "request_uuid", + "request_method", "request_scheme", "response_code", "path"] +} + +LOG_MIDDLEWARE_TESTCLIENT_FHIR_NAVIGATION_EVENT_SCHEMA = { + "title": "RequestResponseLogSchema", + "type": "object", + "properties": { + "type": {"pattern": "request_response_middleware"}, + "size": {"type": "integer"}, + "start_time": {"type": "number"}, + "end_time": {"type": "number"}, + "ip_addr": {"type": "string", "format": "ip-address"}, + "request_uuid": {"type": "string", "format": "uuid"}, + "request_method": {"pattern": "GET"}, + "request_scheme": {"pattern": "http"}, + "response_code": {"type": "integer", "enum": [status.HTTP_200_OK]}, + "path": {"pattern": "/testclient/.+"}, + "req_qparam__count": {"type": "string"}, + "req_qparam_patient": {"type": "string"}, + "req_qparam_beneficiary": {"type": "string"}, + "req_qparam_startindex": {"type": "string"}, + }, + "required": ["type", "size", "start_time", "end_time", "ip_addr", "request_uuid", + "request_method", "request_scheme", "response_code", "path", + "req_qparam__count", "req_qparam_startindex"] +} + +LOG_MIDDLEWARE_FHIR_SEARCH_EVENT_SCHEMA = { + "title": "RequestResponseLogSchema", + "type": "object", + "properties": { + "type": {"pattern": "request_response_middleware"}, + "size": {"type": "integer"}, + "start_time": {"type": "number"}, + "end_time": {"type": "number"}, + "ip_addr": {"type": "string", "format": "ip-address"}, + "request_uuid": {"type": "string", "format": "uuid"}, + "request_method": {"pattern": "GET"}, + "request_scheme": {"pattern": "http"}, + "response_code": {"type": "integer", "enum": [status.HTTP_200_OK]}, + "req_user_id": {"type": "integer"}, + "req_user_username": {"pattern": "fred"}, + "req_fhir_id": {"type": "string"}, + "req_qparam_format": {"pattern": "json"}, + "req_qparam_patient": {"type": "string"}, + "req_qparam_beneficiary": {"type": "string"}, + "path": {"pattern": "/v1/fhir/.+"}, + "user": {"type": "string"}, + "fhir_id": {"type": "string"}, + "access_token_scopes": {"type": "string"}, + "access_token_id": {"type": "number"}, + "app_require_demographic_scopes": {"type": "boolean"}, + "user_id": {"type": "integer"}, + "user_username": {"pattern": "fred"}, + "fhir_bundle_type": {"pattern": "searchset|null"}, + "fhir_resource_id": {"type": "string"}, + "fhir_resource_type": {"pattern": "Bundle|Patient|Coverage|ExplanationOfBenefit"}, + "fhir_attribute_count": {"type": "number"}, + "fhir_entry_count": {"type": ["number", "null"]}, + "fhir_total": {"type": ["number", "null"]}, + }, + "required": ["type", "size", "start_time", "end_time", "ip_addr", "request_uuid", + "request_method", "request_scheme", "response_code", + "req_user_id", "req_user_username", "req_fhir_id", "req_qparam_format", + "path", "user", "fhir_id", "access_token_scopes", "access_token_id", "app_require_demographic_scopes", + "user_id", "user_username", "fhir_bundle_type", "fhir_resource_id", "fhir_resource_type", + "fhir_attribute_count", "fhir_entry_count", "fhir_total"] +} + +LOG_MIDDLEWARE_FHIR_NAVIGATION_EVENT_SCHEMA = { + "title": "RequestResponseLogSchema", + "type": "object", + "properties": { + "type": {"pattern": "request_response_middleware"}, + "size": {"type": "integer"}, + "start_time": {"type": "number"}, + "end_time": {"type": "number"}, + "ip_addr": {"type": "string", "format": "ip-address"}, + "request_uuid": {"type": "string", "format": "uuid"}, + "request_method": {"pattern": "GET"}, + "request_scheme": {"pattern": "http"}, + "response_code": {"type": "integer", "enum": [status.HTTP_200_OK]}, + "req_user_id": {"type": "integer"}, + "req_user_username": {"pattern": "fred"}, + "req_fhir_id": {"type": "string"}, + "req_qparam__count": {"type": "string"}, + "req_qparam_format": {"pattern": "json"}, + "req_qparam_patient": {"type": "string"}, + "req_qparam_beneficiary": {"type": "string"}, + "req_qparam_startindex": {"type": "string"}, + "path": {"pattern": "/v1/fhir/.+"}, + "user": {"type": "string"}, + "fhir_id": {"type": "string"}, + "access_token_scopes": {"type": "string"}, + "access_token_id": {"type": "number"}, + "app_require_demographic_scopes": {"type": "boolean"}, + "user_id": {"type": "integer"}, + "user_username": {"pattern": "fred"}, + "fhir_bundle_type": {"pattern": "searchset|null"}, + "fhir_resource_id": {"type": "string"}, + "fhir_resource_type": {"pattern": "Bundle|Patient|Coverage|ExplanationOfBenefit"}, + "fhir_attribute_count": {"type": "number"}, + "fhir_entry_count": {"type": ["number", "null"]}, + "fhir_total": {"type": ["number", "null"]}, + }, + "required": ["type", "size", "start_time", "end_time", "ip_addr", + "request_uuid", "request_method", "request_scheme", "response_code", + "req_user_id", "req_user_username", "req_fhir_id", + "req_qparam__count", "req_qparam_format", "req_qparam_startindex", + "path", "user", "fhir_id", "access_token_scopes", "access_token_id", "app_require_demographic_scopes", + "user_id", "user_username", "fhir_bundle_type", "fhir_resource_id", "fhir_resource_type", + "fhir_attribute_count", "fhir_entry_count", "fhir_total"] +} + +LOG_MIDDLEWARE_FHIR_READ_EVENT_SCHEMA = { + "title": "RequestResponseLogSchema", + "type": "object", + "properties": { + "type": {"pattern": "request_response_middleware"}, + "size": {"type": "integer"}, + "start_time": {"type": "number"}, + "end_time": {"type": "number"}, + "ip_addr": {"type": "string", "format": "ip-address"}, + "request_uuid": {"type": "string", "format": "uuid"}, + "request_method": {"pattern": "GET"}, + "request_scheme": {"pattern": "http"}, + "response_code": {"type": "integer", "enum": [status.HTTP_200_OK]}, + "req_user_id": {"type": "integer"}, + "req_user_username": {"pattern": "fred"}, + "req_fhir_id": {"type": "string"}, + "path": {"pattern": "/v1/fhir/.+"}, + "user": {"type": "string"}, + "fhir_id": {"type": "string"}, + "access_token_scopes": {"type": "string"}, + "access_token_id": {"type": "number"}, + "app_require_demographic_scopes": {"type": "boolean"}, + "user_id": {"type": "integer"}, + "user_username": {"pattern": "fred"}, + "fhir_bundle_type": {"pattern": "searchset|null"}, + "fhir_resource_id": {"type": "string"}, + "fhir_resource_type": {"pattern": "Bundle|Patient|Coverage|ExplanationOfBenefit"}, + "fhir_attribute_count": {"type": "number"}, + "fhir_entry_count": {"type": ["number", "null"]}, + "fhir_total": {"type": ["number", "null"]}, + }, + "required": ["type", "size", "start_time", "end_time", "ip_addr", + "request_uuid", "request_method", "request_scheme", "response_code", + "req_user_id", "req_user_username", "req_fhir_id", + "path", "user", "fhir_id", "access_token_scopes", "access_token_id", "app_require_demographic_scopes", + "user_id", "user_username", "fhir_bundle_type", "fhir_resource_id", "fhir_resource_type", + "fhir_attribute_count", "fhir_entry_count", "fhir_total"] +} + +LOG_MIDDLEWARE_FHIR_USERINFO_EVENT_SCHEMA = { + "title": "RequestResponseLogSchema", + "type": "object", + "properties": { + "type": {"pattern": "request_response_middleware"}, + "size": {"type": "integer"}, + "start_time": {"type": "number"}, + "end_time": {"type": "number"}, + "ip_addr": {"type": "string", "format": "ip-address"}, + "request_uuid": {"type": "string", "format": "uuid"}, + "request_method": {"pattern": "GET"}, + "request_scheme": {"pattern": "http"}, + "response_code": {"type": "integer", "enum": [status.HTTP_200_OK]}, + "req_user_id": {"type": "integer"}, + "req_user_username": {"pattern": "fred"}, + "req_fhir_id": {"type": "string"}, + "path": {"pattern": "/v1/connect/userinfo"}, + "user": {"type": "string"}, + "fhir_id": {"type": "string"}, + "access_token_scopes": {"type": "string"}, + "access_token_id": {"type": "number"}, + "app_require_demographic_scopes": {"type": "boolean"}, + "user_id": {"type": "integer"}, + "user_username": {"pattern": "fred"}, + }, + "required": ["type", "size", "start_time", "end_time", "ip_addr", "request_uuid", + "request_method", "request_scheme", "response_code", + "req_user_id", "req_user_username", "req_fhir_id", + "path", "user", "fhir_id", "access_token_scopes", + "access_token_id", "app_require_demographic_scopes", + "user_id", "user_username"] +} + +LOG_MIDDLEWARE_TESTCLIENT_MISCINFO_EVENT_SCHEMA = { + "title": "RequestResponseLogSchema", + "type": "object", + "properties": { + "type": {"pattern": "request_response_middleware"}, + "size": {"type": "integer"}, + "start_time": {"type": "number"}, + "end_time": {"type": "number"}, + "ip_addr": {"type": "string", "format": "ip-address"}, + "request_uuid": {"type": "string", "format": "uuid"}, + "request_method": {"pattern": "GET"}, + "request_scheme": {"pattern": "http"}, + "response_code": {"type": "integer", "enum": [status.HTTP_200_OK]}, + "path": {"pattern": "/testclient/userinfo|/testclient/openidConfig|/testclient/metadata|/testclient/restart"}, + }, + "required": ["type", "size", "start_time", "end_time", "ip_addr", "request_uuid", + "request_method", "request_scheme", "response_code", "path"] +} diff --git a/apps/integration_tests/logging_tests.py b/apps/integration_tests/logging_tests.py new file mode 100755 index 000000000..cedcbcc79 --- /dev/null +++ b/apps/integration_tests/logging_tests.py @@ -0,0 +1,614 @@ +import copy +import json +import jsonschema +import os +import re +import time + +from django.conf import settings +from django.test import TestCase +from enum import Enum +from json.decoder import JSONDecodeError +from jsonschema import validate + +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.support.wait import WebDriverWait +from selenium.webdriver.common.desired_capabilities import DesiredCapabilities +from selenium.webdriver.common.keys import Keys +from apps.integration_tests.log_event_schemas import ( + LOG_MIDDLEWARE_FHIR_READ_EVENT_SCHEMA, + LOG_MIDDLEWARE_FHIR_SEARCH_EVENT_SCHEMA, + LOG_MIDDLEWARE_FHIR_NAVIGATION_EVENT_SCHEMA, + LOG_MIDDLEWARE_FHIR_USERINFO_EVENT_SCHEMA, + LOG_MIDDLEWARE_POST_EVENT_SCHEMA, + LOG_MIDDLEWARE_TESTCLIENT_FHIR_NAVIGATION_EVENT_SCHEMA, + LOG_MIDDLEWARE_TESTCLIENT_FHIR_READ_EVENT_SCHEMA, + LOG_MIDDLEWARE_TESTCLIENT_FHIR_SEARCH_EVENT_SCHEMA, + LOG_MIDDLEWARE_ACCESS_GRANT_EVENT_SCHEMA, + LOG_MIDDLEWARE_AUTHORIZE_EVENT_SCHEMA, + LOG_MIDDLEWARE_EVENT_SCHEMA, + LOG_MIDDLEWARE_MEDICARE_CALLBACK_EVENT_SCHEMA, + LOG_MIDDLEWARE_MEDICARE_LOGIN_EVENT_SCHEMA, + LOG_MIDDLEWARE_TESTCLIENT_AUTHLINK_EVENT_SCHEMA, + LOG_MIDDLEWARE_AUTH_START_EVENT_SCHEMA, + LOG_MIDDLEWARE_TESTCLIENT_MISCINFO_EVENT_SCHEMA +) + +TEST_LOGGING_FILE = "./docker-compose/tmp/bb2_logging_test.log" +MIDDLEWARE_LOG_EVENT_TYPE = "request_response_middleware" + +EXPECTED_LOGGING_EVENTS = [ + { + # clicked test client + "schema": LOG_MIDDLEWARE_EVENT_SCHEMA, + "path": "/testclient/", + }, + { + # v1 auth link + "schema": LOG_MIDDLEWARE_TESTCLIENT_AUTHLINK_EVENT_SCHEMA, + "path": "/testclient/authorize-link", + }, + { + # authorize as a bene + "schema": LOG_MIDDLEWARE_AUTH_START_EVENT_SCHEMA, + "path": "/v1/o/authorize/", + }, + { + "schema": LOG_MIDDLEWARE_MEDICARE_LOGIN_EVENT_SCHEMA, + "path": "/mymedicare/login" + }, + { + "schema": LOG_MIDDLEWARE_MEDICARE_CALLBACK_EVENT_SCHEMA, + "path": "/mymedicare/sls-callback", + }, + { + "schema": LOG_MIDDLEWARE_AUTHORIZE_EVENT_SCHEMA, + "path_regex": "/v1/o/authorize/.+/" + }, + { + "schema": LOG_MIDDLEWARE_ACCESS_GRANT_EVENT_SCHEMA, + "path_regex": "/v1/o/authorize/.+/" + }, + { + "schema": LOG_MIDDLEWARE_POST_EVENT_SCHEMA, + "path": "/v1/o/token/", + }, + { + "schema": LOG_MIDDLEWARE_EVENT_SCHEMA, + "path": "/v1/connect/userinfo", + }, + { + "schema": LOG_MIDDLEWARE_EVENT_SCHEMA, + "path": "/testclient/callback", + # "response_code": 301 + }, + { + # redirect to test client fhir links page + "schema": LOG_MIDDLEWARE_EVENT_SCHEMA, + "path": "/testclient/" + }, + { + "schema": LOG_MIDDLEWARE_FHIR_READ_EVENT_SCHEMA, + "path": "/v1/fhir/Patient/-20140000008325" + }, + { + "schema": LOG_MIDDLEWARE_TESTCLIENT_FHIR_READ_EVENT_SCHEMA, + "path": "/testclient/Patient" + }, + { + # first Coverage + "schema": LOG_MIDDLEWARE_FHIR_SEARCH_EVENT_SCHEMA, + "path": "/v1/fhir/Coverage/" + }, + { + "schema": LOG_MIDDLEWARE_TESTCLIENT_FHIR_SEARCH_EVENT_SCHEMA, + "path": "/testclient/Coverage" + }, + { + # last Coverage + "schema": LOG_MIDDLEWARE_FHIR_NAVIGATION_EVENT_SCHEMA, + "path": "/v1/fhir/Coverage/" + }, + { + "schema": LOG_MIDDLEWARE_TESTCLIENT_FHIR_NAVIGATION_EVENT_SCHEMA, + "path": "/testclient/Coverage" + }, + { + # test client fhir links page + "schema": LOG_MIDDLEWARE_EVENT_SCHEMA, + "path": "/testclient/" + }, + { + # first EOB + "schema": LOG_MIDDLEWARE_FHIR_SEARCH_EVENT_SCHEMA, + "path": "/v1/fhir/ExplanationOfBenefit/" + }, + { + "schema": LOG_MIDDLEWARE_TESTCLIENT_FHIR_SEARCH_EVENT_SCHEMA, + "path": "/testclient/ExplanationOfBenefit" + }, + { + # last EOB + "schema": LOG_MIDDLEWARE_FHIR_NAVIGATION_EVENT_SCHEMA, + "path": "/v1/fhir/ExplanationOfBenefit/" + }, + { + "schema": LOG_MIDDLEWARE_TESTCLIENT_FHIR_NAVIGATION_EVENT_SCHEMA, + "path": "/testclient/ExplanationOfBenefit" + }, + { + # test client fhir links page + "schema": LOG_MIDDLEWARE_EVENT_SCHEMA, + "path": "/testclient/" + }, + { + # userinfo ep + "schema": LOG_MIDDLEWARE_FHIR_USERINFO_EVENT_SCHEMA, + "path": "/v1/connect/userinfo" + }, + { + # userinfo testclient url + "schema": LOG_MIDDLEWARE_TESTCLIENT_MISCINFO_EVENT_SCHEMA, + "path": "/testclient/userinfo" + }, + { + # meta data + "schema": LOG_MIDDLEWARE_TESTCLIENT_MISCINFO_EVENT_SCHEMA, + "path": "/testclient/metadata" + }, + { + # openid discovery + "schema": LOG_MIDDLEWARE_TESTCLIENT_MISCINFO_EVENT_SCHEMA, + "path": "/testclient/openidConfig" + }, + { + # restart test client - go to test client home page with v1, v2 get sample token buttons + "schema": LOG_MIDDLEWARE_TESTCLIENT_MISCINFO_EVENT_SCHEMA, + "path": "/testclient/restart" + }, + +] + +TESTCLIENT_BUNDLE_LABEL_FMT = "Response (Bundle of {}), API version: {}" +TESTCLIENT_RESOURCE_LABEL_FMT = "Response ({}), API version: {}" +MESSAGE_NO_PERMISSION = "You do not have permission to perform this action." +TESTCASE_BANNER_FMT = "** {} TEST: {}, API: {}, STEP: {}, {}" +''' +UI Widget text: texts on e.g. buttons, links, labels etc. +''' +LNK_TXT_TESTCLIENT = "Test Client" +LNK_TXT_GET_TOKEN_V1 = "Get a Sample Authorization Token" +LNK_TXT_GET_TOKEN_V2 = "Get a Sample Authorization Token for v2" +LNK_TXT_AUTH_AS_BENE = "Authorize as a Beneficiary" +LNK_TXT_RESTART_TESTCLIENT = "restart testclient" +# FHIR search result bundle pagination +LNK_TXT_NAV_FIRST = "first" +LNK_TXT_NAV_NEXT = "next" +LNK_TXT_NAV_PREV = "previous" +LNK_TXT_NAV_LAST = "last" +LNK_TXT_NAV_SELF = "self" +# FHIR resources query page +LNK_TXT_PATIENT = "Patient" +LNK_TXT_EOB = "ExplanationOfBenefit" +LNK_TXT_COVERAGE = "Coverage" +LNK_TXT_PROFILE = "Profile" +LNK_TXT_METADATA = "FHIR Metadata" +LNK_TXT_OIDC_DISCOVERY = "OIDC Discovery" +# FHIR result page label H2 +LAB_FHIR_RESULTPAGE_H2 = "h2" +CONTENT_FHIR_RESULTPAGE_PRE = "pre" +# MSLSX login form +TXT_FLD_SUB_MSLSX = "username" +TXT_FLD_HICN_MSLSX = "hicn" +TXT_FLD_MBI_MSLSX = "mbi" +TXT_FLD_VAL_SUB_MSLSX = "fred" +MSLSX_TXT_FLD_HICN_VAL = "1000044680" +MSLSX_TXT_FLD_MBI_VAL = "2SW4N00AA00" +MSLSX_CSS_BUTTON = "button" +# SLSX login form +SLSX_TXT_FLD_USERNAME = "username-textbox" +SLSX_TXT_FLD_PASSWORD = "password-textbox" +SLSX_TXT_FLD_USERNAME_VAL = "BBUser00000" +SLSX_TXT_FLD_PASSWORD_VAL = "PW00000!" +SLSX_CSS_BUTTON = "login-button" +# Demographic info access grant form +BTN_ID_GRANT_DEMO_ACCESS = "approve" +BTN_ID_DENY_DEMO_ACCESS = "deny" +BTN_ID_RADIO_NOT_SHARE = "label:nth-child(5)" +# API versions +API_V2 = "v2" +API_V1 = "v1" + + +class Action(Enum): + LOAD_PAGE = 1 + FIND_CLICK = 2 + FIND = 3 + FIND_SEND_KEY = 4 + CHECK = 5 + BACK = 6 + LOGIN = 7 + CONTAIN_TEXT = 8 + GET_SAMPLE_TOKEN_START = 9 + SLEEP = 10 + + +BROWSERBACK = { + "display": "Back to FHIR resource page", + "action": Action.BACK, +} + +WAIT_SECONDS = { + "display": "Sleep seconds...", + "action": Action.SLEEP, + "params": [3], +} + +CHECK_TESTCLIENT_START_PAGE = { + "display": "Check it's on 'Test Client' start page", + "action": Action.FIND, + "params": [30, By.LINK_TEXT, LNK_TXT_GET_TOKEN_V1] +} + +CLICK_TESTCLIENT = { + "display": "Click link 'Test Client'", + "action": Action.FIND_CLICK, + "params": [30, By.LINK_TEXT, LNK_TXT_TESTCLIENT] +} + +CLICK_RADIO_NOT_SHARE = { + "display": "Click 'Share healthcare data, but not your personal info' on DEMO info grant form", + "action": Action.FIND_CLICK, + "params": [20, By.CSS_SELECTOR, BTN_ID_RADIO_NOT_SHARE] +} + +CLICK_AGREE_ACCESS = { + "display": "Click 'Agree' on DEMO info grant form", + "action": Action.FIND_CLICK, + "params": [20, By.ID, BTN_ID_GRANT_DEMO_ACCESS] +} + +CLICK_DENY_ACCESS = { + "display": "Click 'Deny' on DEMO info grant form", + "action": Action.FIND_CLICK, + "params": [20, By.ID, BTN_ID_DENY_DEMO_ACCESS] +} + +CALL_LOGIN = { + "display": "Start login ...", + "action": Action.LOGIN, +} + +SEQ_LOGIN_MSLSX = [ + { + "display": "Input SUB(username)", + "action": Action.FIND_SEND_KEY, + "params": [20, By.NAME, TXT_FLD_SUB_MSLSX, TXT_FLD_VAL_SUB_MSLSX] + }, + { + "display": "Input hicn", + "action": Action.FIND_SEND_KEY, + "params": [20, By.NAME, TXT_FLD_HICN_MSLSX, MSLSX_TXT_FLD_HICN_VAL] + }, + { + "display": "Input mbi", + "action": Action.FIND_SEND_KEY, + "params": [20, By.NAME, TXT_FLD_MBI_MSLSX, MSLSX_TXT_FLD_MBI_VAL] + }, + { + "display": "Click 'submit' on MSLSX login form", + "action": Action.FIND_CLICK, + "params": [20, By.CSS_SELECTOR, MSLSX_CSS_BUTTON] + }, +] + +SEQ_LOGIN_SLSX = [ + { + "display": "MyMedicare login username", + "action": Action.FIND_SEND_KEY, + "params": [20, By.ID, SLSX_TXT_FLD_USERNAME, SLSX_TXT_FLD_USERNAME_VAL] + }, + { + "display": "MyMedicare login password", + "action": Action.FIND_SEND_KEY, + "params": [20, By.ID, SLSX_TXT_FLD_PASSWORD, SLSX_TXT_FLD_PASSWORD_VAL] + }, + { + "display": "Click 'submit' on SLSX login form", + "action": Action.FIND_CLICK, + "params": [20, By.ID, SLSX_CSS_BUTTON] + }, +] + +SEQ_AUTHORIZE_START = [ + { + "display": "Load BB2 Landing Page ...", + "action": Action.LOAD_PAGE, + "params": [settings.HOSTNAME_URL] + }, + CLICK_TESTCLIENT, + { + "display": "Click link to get sample token v1/v2", + "action": Action.GET_SAMPLE_TOKEN_START, + }, + { + "display": "Click link 'Authorize as a Beneficiary' - start authorization", + "action": Action.FIND_CLICK, + "params": [30, By.LINK_TEXT, LNK_TXT_AUTH_AS_BENE] + }, +] + +SEQ_QUERY_FHIR_RESOURCES = [ + { + "display": "Click 'Patient' on FHIR resources page", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_PATIENT] + }, + { + "display": "Check Patient result page title", + "action": Action.CHECK, + "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, LNK_TXT_PATIENT] + }, + BROWSERBACK, + { + "display": "Click 'Coverage' on FHIR resources page", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_COVERAGE] + }, + { + "display": "Check Coverage result page title", + "action": Action.CHECK, + "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_BUNDLE_LABEL_FMT, LNK_TXT_COVERAGE] + }, + { + "display": "Check and click Coverage result page navigation links 'last'", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_NAV_LAST] + }, + CLICK_TESTCLIENT, + { + "display": "Click 'ExplanationOfBenefit' on FHIR resources page", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_EOB] + }, + { + "display": "Check ExplanationOfBenefit result page title", + "action": Action.CHECK, + "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_BUNDLE_LABEL_FMT, LNK_TXT_EOB] + }, + { + "display": "Check and click ExplanationOfBenefit result page navigation links 'last'", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_NAV_LAST] + }, + WAIT_SECONDS, + CLICK_TESTCLIENT, + WAIT_SECONDS, + { + "display": "Click 'Profile' on FHIR resources page", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_PROFILE] + }, + WAIT_SECONDS, + { + "display": "Check Profile result page title", + "action": Action.CHECK, + "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, + "{} (OIDC Userinfo)".format(LNK_TXT_PROFILE)] + }, + BROWSERBACK, + { + "display": "Click 'FHIR Metadata' on FHIR resources page", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_METADATA] + }, + { + "display": "Check FHIR Metadata result page title", + "action": Action.CHECK, + "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, LNK_TXT_METADATA] + }, + BROWSERBACK, + { + "display": "Click 'OIDC Discovery' on FHIR resources page", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_OIDC_DISCOVERY] + }, + { + "display": "Check OIDC Discovery result page title", + "action": Action.CHECK, + "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, LNK_TXT_OIDC_DISCOVERY] + }, + BROWSERBACK, +] + +TESTS = { + "auth_fhir_flow_calls_logging": [ + {"sequence": SEQ_AUTHORIZE_START}, + CALL_LOGIN, + CLICK_AGREE_ACCESS, + {"sequence": SEQ_QUERY_FHIR_RESOURCES} + ], +} + + +class LoggingTests(TestCase): + ''' + Test loggings generated from authorization and fhir flow using the built in testclient as + the driver (selenium) + ''' + wait_completed = False + + def _validateJsonSchema(self, schema, content): + # print("SCHEMA={}".format(schema)) + # print("CONTENT={}".format(content)) + try: + validate(instance=content, schema=schema) + except jsonschema.exceptions.ValidationError as e: + # Show error info for debugging + print("jsonschema.exceptions.ValidationError: ", e) + return False + return True + + def setUp(self): + super(LoggingTests, self).setUp() + # a bit waiting for selenium service ready for sure + if not LoggingTests.wait_completed: + time.sleep(20) + LoggingTests.wait_completed = True + print("set wait_completed={}".format(LoggingTests.wait_completed)) + else: + print("wait_completed={}".format(LoggingTests.wait_completed)) + + opt = webdriver.ChromeOptions() + opt.add_argument('--headless') + opt.add_argument("--disable-dev-shm-usage") + opt.add_argument("--disable-web-security") + opt.add_argument("--allow-running-insecure-content") + opt.add_argument("--no-sandbox") + opt.add_argument("--disable-setuid-sandbox") + opt.add_argument("--disable-webgl") + opt.add_argument("--disable-popup-blocking") + opt.add_argument("--enable-javascript") + opt.add_argument('--allow-insecure-localhost') + opt.add_argument('--window-size=1920,1080') + opt.add_argument("--whitelisted-ips=''") + + self.driver = webdriver.Remote( + command_executor='http://selenium-hub:4444', + desired_capabilities=DesiredCapabilities.CHROME, options=opt) + + self.actions = { + Action.LOAD_PAGE: self._load_page, + Action.FIND_CLICK: self._find_and_click, + Action.FIND: self._find_and_return, + Action.FIND_SEND_KEY: self._find_and_sendkey, + Action.CHECK: self._check_page_title, + Action.CONTAIN_TEXT: self._check_page_content, + Action.GET_SAMPLE_TOKEN_START: self._click_get_sample_token, + Action.BACK: self._back, + Action.LOGIN: self._login, + Action.SLEEP: self._sleep, + } + self.use_mslsx = os.environ['USE_MSLSX'] + self.login_seq = SEQ_LOGIN_MSLSX if self.use_mslsx == 'true' else SEQ_LOGIN_SLSX + + def tearDown(self): + self.driver.quit() + super(LoggingTests, self).tearDown() + + def _find_and_click(self, timeout_sec, by, by_expr, **kwargs): + elem = WebDriverWait(self.driver, timeout_sec).until(EC.visibility_of_element_located((by, by_expr))) + self.assertIsNotNone(elem) + elem.click() + return elem + + def _testclient_home(self, **kwargs): + return self._find_and_click(30, By.LINK_TEXT, LNK_TXT_RESTART_TESTCLIENT, **kwargs) + + def _find_and_sendkey(self, timeout_sec, by, by_expr, txt, **kwargs): + elem = WebDriverWait(self.driver, timeout_sec).until(EC.visibility_of_element_located((by, by_expr))) + self.assertIsNotNone(elem) + elem.send_keys(txt) + return elem + + def _click_get_sample_token(self, **kwargs): + return self._find_and_click(30, By.LINK_TEXT, + LNK_TXT_GET_TOKEN_V2 if kwargs.get("api_ver", API_V1) == API_V2 else LNK_TXT_GET_TOKEN_V1) + + def _find_and_return(self, timeout_sec, by, by_expr, **kwargs): + elem = WebDriverWait(self.driver, timeout_sec).until(EC.visibility_of_element_located((by, by_expr))) + self.assertIsNotNone(elem) + return elem + + def _load_page(self, url, **kwargs): + self.driver.get(url) + + def _check_page_title(self, timeout_sec, by, by_expr, fmt, resource_type, **kwargs): + elem = self._find_and_return(timeout_sec, by, by_expr, **kwargs) + if not (elem.text == fmt.format(resource_type, kwargs.get("api_ver"))): + print("PAGE:{}".format(self.driver.page_source)) + self.assertEqual(elem.text, fmt.format(resource_type, kwargs.get("api_ver"))) + + def _check_page_content(self, timeout_sec, by, by_expr, content_txt, **kwargs): + elem = self._find_and_return(timeout_sec, by, by_expr, **kwargs) + self.assertIn(content_txt, elem.text) + + def _back(self, **kwargs): + self.driver.back() + + def _sleep(self, sec, **kwargs): + time.sleep(sec) + + def _login(self, step, **kwargs): + if self.use_mslsx == 'false': + # dismiss mymedicare popup if present + webdriver.ActionChains(self.driver).send_keys(Keys.ESCAPE).perform() + self._play(self.login_seq, step, **kwargs) + + def _print_testcase_banner(self, test_name, api_ver, step_0, id_service, start=True): + print() + print("******************************************************************") + print(TESTCASE_BANNER_FMT.format("START" if start else "END", test_name, api_ver, step_0, + "MSLSX" if id_service == 'true' else "SLSX")) + print("******************************************************************") + print() + + def _play(self, lst, step, **kwargs): + for s in lst: + seq = s.get("sequence") + # expects sequence of actions or action + if seq is not None: + self._play(seq, step, **kwargs) + else: + # single action + action = s.get('action', None) + step[0] = step[0] + 1 + if action is not None: + print("{}:{}:".format(step[0], s.get("display", "Not available"))) + if action == Action.LOGIN: + self.actions[action](*s.get("params", []), step, **kwargs) + else: + self.actions[action](*s.get("params", []), **kwargs) + else: + raise ValueError("Invalid test case, expect dict with action...") + + def test_auth_fhir_flows_logging(self): + step = [0] + test_name = "auth_fhir_flow_calls_logging" + api_ver = API_V1 + self._print_testcase_banner(test_name, api_ver, step[0], self.use_mslsx, True) + self._play(TESTS[test_name], step, api_ver=api_ver) + self._testclient_home() + self._validate_events() + self._print_testcase_banner(test_name, api_ver, step[0], self.use_mslsx, False) + + def _validate_events(self): + # validate middleware logging records in log file ./docker-compose/tmp/bb2_logging_test.log + with open(TEST_LOGGING_FILE, 'r') as f: + log_records = f.readlines() + start_validation = False + expected_events = copy.deepcopy(EXPECTED_LOGGING_EVENTS) + while log_records: + r = log_records.pop(0) + try: + event_json = json.loads(r) + if event_json.get('type', 'NO TYPE INFO') == MIDDLEWARE_LOG_EVENT_TYPE: + p = event_json.get('path', None) + if not start_validation: + if p == "/": + start_validation = True + else: + event_desc = expected_events.pop(0) + if event_desc.get('path_regex') is not None: + self.assertTrue(re.match(event_desc.get('path_regex'), p)) + else: + self.assertEqual(p, event_desc.get('path')) + self.assertTrue(self._validateJsonSchema(event_desc.get('schema'), event_json)) + except JSONDecodeError: + # skip non json line + pass + + # all log events present and validated + self.assertEqual(len(expected_events), 0) diff --git a/apps/integration_tests/selenium_tests.py b/apps/integration_tests/selenium_tests.py new file mode 100755 index 000000000..15dee6b1b --- /dev/null +++ b/apps/integration_tests/selenium_tests.py @@ -0,0 +1,551 @@ +import os +import time +from django.conf import settings +from django.test import TestCase +from enum import Enum + +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.support.wait import WebDriverWait +from selenium.webdriver.common.desired_capabilities import DesiredCapabilities +from selenium.webdriver.common.keys import Keys + +TESTCLIENT_BUNDLE_LABEL_FMT = "Response (Bundle of {}), API version: {}" +TESTCLIENT_RESOURCE_LABEL_FMT = "Response ({}), API version: {}" +MESSAGE_NO_PERMISSION = "You do not have permission to perform this action." +TESTCASE_BANNER_FMT = "** {} TEST: {}, API: {}, STEP: {}, {}" +''' +UI Widget text: texts on e.g. buttons, links, labels etc. +''' +LNK_TXT_TESTCLIENT = "Test Client" +LNK_TXT_GET_TOKEN_V1 = "Get a Sample Authorization Token" +LNK_TXT_GET_TOKEN_V2 = "Get a Sample Authorization Token for v2" +LNK_TXT_AUTH_AS_BENE = "Authorize as a Beneficiary" +LNK_TXT_RESTART_TESTCLIENT = "restart testclient" +# FHIR search result bundle pagination +LNK_TXT_NAV_FIRST = "first" +LNK_TXT_NAV_NEXT = "next" +LNK_TXT_NAV_PREV = "previous" +LNK_TXT_NAV_LAST = "last" +LNK_TXT_NAV_SELF = "self" +# FHIR resources query page +LNK_TXT_PATIENT = "Patient" +LNK_TXT_EOB = "ExplanationOfBenefit" +LNK_TXT_COVERAGE = "Coverage" +LNK_TXT_PROFILE = "Profile" +LNK_TXT_METADATA = "FHIR Metadata" +LNK_TXT_OIDC_DISCOVERY = "OIDC Discovery" +# FHIR result page label H2 +LAB_FHIR_RESULTPAGE_H2 = "h2" +CONTENT_FHIR_RESULTPAGE_PRE = "pre" +# MSLSX login form +TXT_FLD_SUB_MSLSX = "username" +TXT_FLD_HICN_MSLSX = "hicn" +TXT_FLD_MBI_MSLSX = "mbi" +TXT_FLD_VAL_SUB_MSLSX = "fred" +MSLSX_TXT_FLD_HICN_VAL = "1000044680" +MSLSX_TXT_FLD_MBI_VAL = "2SW4N00AA00" +MSLSX_CSS_BUTTON = "button" +# SLSX login form +SLSX_TXT_FLD_USERNAME = "username-textbox" +SLSX_TXT_FLD_PASSWORD = "password-textbox" +SLSX_TXT_FLD_USERNAME_VAL = "BBUser00000" +SLSX_TXT_FLD_PASSWORD_VAL = "PW00000!" +SLSX_CSS_BUTTON = "login-button" +# Demographic info access grant form +BTN_ID_GRANT_DEMO_ACCESS = "approve" +BTN_ID_DENY_DEMO_ACCESS = "deny" +BTN_ID_RADIO_NOT_SHARE = "label:nth-child(5)" +# API versions +API_V2 = "v2" +API_V1 = "v1" + + +class Action(Enum): + LOAD_PAGE = 1 + FIND_CLICK = 2 + FIND = 3 + FIND_SEND_KEY = 4 + CHECK = 5 + BACK = 6 + LOGIN = 7 + CONTAIN_TEXT = 8 + GET_SAMPLE_TOKEN_START = 9 + SLEEP = 10 + + +BROWSERBACK = { + "display": "Back to FHIR resource page", + "action": Action.BACK, +} + +WAIT_SECONDS = { + "display": "Sleep seconds...", + "action": Action.SLEEP, + "params": [3], +} + +CHECK_TESTCLIENT_START_PAGE = { + "display": "Check it's on 'Test Client' start page", + "action": Action.FIND, + "params": [30, By.LINK_TEXT, LNK_TXT_GET_TOKEN_V1] +} + +CLICK_TESTCLIENT = { + "display": "Click link 'Test Client'", + "action": Action.FIND_CLICK, + "params": [30, By.LINK_TEXT, LNK_TXT_TESTCLIENT] +} + +CLICK_RADIO_NOT_SHARE = { + "display": "Click 'Share healthcare data, but not your personal info' on DEMO info grant form", + "action": Action.FIND_CLICK, + "params": [20, By.CSS_SELECTOR, BTN_ID_RADIO_NOT_SHARE] +} + +CLICK_AGREE_ACCESS = { + "display": "Click 'Agree' on DEMO info grant form", + "action": Action.FIND_CLICK, + "params": [20, By.ID, BTN_ID_GRANT_DEMO_ACCESS] +} + +CLICK_DENY_ACCESS = { + "display": "Click 'Deny' on DEMO info grant form", + "action": Action.FIND_CLICK, + "params": [20, By.ID, BTN_ID_DENY_DEMO_ACCESS] +} + +CALL_LOGIN = { + "display": "Start login ...", + "action": Action.LOGIN, +} + +SEQ_LOGIN_MSLSX = [ + { + "display": "Input SUB(username)", + "action": Action.FIND_SEND_KEY, + "params": [20, By.NAME, TXT_FLD_SUB_MSLSX, TXT_FLD_VAL_SUB_MSLSX] + }, + { + "display": "Input hicn", + "action": Action.FIND_SEND_KEY, + "params": [20, By.NAME, TXT_FLD_HICN_MSLSX, MSLSX_TXT_FLD_HICN_VAL] + }, + { + "display": "Input mbi", + "action": Action.FIND_SEND_KEY, + "params": [20, By.NAME, TXT_FLD_MBI_MSLSX, MSLSX_TXT_FLD_MBI_VAL] + }, + { + "display": "Click 'submit' on MSLSX login form", + "action": Action.FIND_CLICK, + "params": [20, By.CSS_SELECTOR, MSLSX_CSS_BUTTON] + }, +] + +SEQ_LOGIN_SLSX = [ + { + "display": "MyMedicare login username", + "action": Action.FIND_SEND_KEY, + "params": [20, By.ID, SLSX_TXT_FLD_USERNAME, SLSX_TXT_FLD_USERNAME_VAL] + }, + { + "display": "MyMedicare login password", + "action": Action.FIND_SEND_KEY, + "params": [20, By.ID, SLSX_TXT_FLD_PASSWORD, SLSX_TXT_FLD_PASSWORD_VAL] + }, + { + "display": "Click 'submit' on SLSX login form", + "action": Action.FIND_CLICK, + "params": [20, By.ID, SLSX_CSS_BUTTON] + }, +] + +SEQ_AUTHORIZE_START = [ + { + "display": "Load BB2 Landing Page ...", + "action": Action.LOAD_PAGE, + "params": [settings.HOSTNAME_URL] + }, + CLICK_TESTCLIENT, + { + "display": "Click link to get sample token v1/v2", + "action": Action.GET_SAMPLE_TOKEN_START, + }, + { + "display": "Click link 'Authorize as a Beneficiary' - start authorization", + "action": Action.FIND_CLICK, + "params": [30, By.LINK_TEXT, LNK_TXT_AUTH_AS_BENE] + }, +] + +SEQ_QUERY_FHIR_RESOURCES = [ + { + "display": "Click 'Patient' on FHIR resources page", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_PATIENT] + }, + { + "display": "Check Patient result page title", + "action": Action.CHECK, + "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, LNK_TXT_PATIENT] + }, + BROWSERBACK, + { + "display": "Click 'Coverage' on FHIR resources page", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_COVERAGE] + }, + { + "display": "Check Coverage result page title", + "action": Action.CHECK, + "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_BUNDLE_LABEL_FMT, LNK_TXT_COVERAGE] + }, + { + "display": "Check and click Coverage result page navigation links 'last'", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_NAV_LAST] + }, + CLICK_TESTCLIENT, + { + "display": "Click 'ExplanationOfBenefit' on FHIR resources page", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_EOB] + }, + { + "display": "Check ExplanationOfBenefit result page title", + "action": Action.CHECK, + "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_BUNDLE_LABEL_FMT, LNK_TXT_EOB] + }, + { + "display": "Check and click ExplanationOfBenefit result page navigation links 'last'", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_NAV_LAST] + }, + WAIT_SECONDS, + CLICK_TESTCLIENT, + WAIT_SECONDS, + { + "display": "Click 'Profile' on FHIR resources page", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_PROFILE] + }, + WAIT_SECONDS, + { + "display": "Check Profile result page title", + "action": Action.CHECK, + "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, + "{} (OIDC Userinfo)".format(LNK_TXT_PROFILE)] + }, + BROWSERBACK, + { + "display": "Click 'FHIR Metadata' on FHIR resources page", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_METADATA] + }, + { + "display": "Check FHIR Metadata result page title", + "action": Action.CHECK, + "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, LNK_TXT_METADATA] + }, + BROWSERBACK, + { + "display": "Click 'OIDC Discovery' on FHIR resources page", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_OIDC_DISCOVERY] + }, + { + "display": "Check OIDC Discovery result page title", + "action": Action.CHECK, + "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, LNK_TXT_OIDC_DISCOVERY] + }, + BROWSERBACK, +] + +SEQ_QUERY_FHIR_RESOURCES_NO_DEMO = [ + { + "display": "Click 'Patient' on FHIR resources page", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_PATIENT] + }, + { + "display": "Check Patient result page content (
) expect no permission message",
+        "action": Action.CONTAIN_TEXT,
+        "params": [20, By.TAG_NAME, CONTENT_FHIR_RESULTPAGE_PRE, MESSAGE_NO_PERMISSION]
+    },
+    BROWSERBACK,
+    {
+        "display": "Click 'Coverage' on FHIR resources page",
+        "action": Action.FIND_CLICK,
+        "params": [20, By.LINK_TEXT, LNK_TXT_COVERAGE]
+    },
+    {
+        "display": "Check Coverage result page title",
+        "action": Action.CHECK,
+        "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_BUNDLE_LABEL_FMT, LNK_TXT_COVERAGE]
+    },
+    {
+        "display": "Check and click Coverage result page navigation links 'last'",
+        "action": Action.FIND_CLICK,
+        "params": [20, By.LINK_TEXT, LNK_TXT_NAV_LAST]
+    },
+    CLICK_TESTCLIENT,
+    {
+        "display": "Click 'ExplanationOfBenefit' on FHIR resources page",
+        "action": Action.FIND_CLICK,
+        "params": [20, By.LINK_TEXT, LNK_TXT_EOB]
+    },
+    {
+        "display": "Check ExplanationOfBenefit result page title",
+        "action": Action.CHECK,
+        "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_BUNDLE_LABEL_FMT, LNK_TXT_EOB]
+    },
+    {
+        "display": "Check and click ExplanationOfBenefit result page navigation links 'last'",
+        "action": Action.FIND_CLICK,
+        "params": [20, By.LINK_TEXT, LNK_TXT_NAV_LAST]
+    },
+    WAIT_SECONDS,
+    CLICK_TESTCLIENT,
+    WAIT_SECONDS,
+    {
+        "display": "Click 'Profile' on FHIR resources page",
+        "action": Action.FIND_CLICK,
+        "params": [20, By.LINK_TEXT, LNK_TXT_PROFILE]
+    },
+    WAIT_SECONDS,
+    {
+        "display": "Check Profile result page content (
) expect no permission message",
+        "action": Action.CONTAIN_TEXT,
+        "params": [20, By.TAG_NAME, CONTENT_FHIR_RESULTPAGE_PRE, MESSAGE_NO_PERMISSION]
+    },
+    BROWSERBACK,
+    {
+        "display": "Click 'FHIR Metadata' on FHIR resources page",
+        "action": Action.FIND_CLICK,
+        "params": [20, By.LINK_TEXT, LNK_TXT_METADATA]
+    },
+    {
+        "display": "Check FHIR Metadata result page title",
+        "action": Action.CHECK,
+        "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, LNK_TXT_METADATA]
+    },
+    BROWSERBACK,
+    {
+        "display": "Click 'OIDC Discovery' on FHIR resources page",
+        "action": Action.FIND_CLICK,
+        "params": [20, By.LINK_TEXT, LNK_TXT_OIDC_DISCOVERY]
+    },
+    {
+        "display": "Check OIDC Discovery result page title",
+        "action": Action.CHECK,
+        "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, LNK_TXT_OIDC_DISCOVERY]
+    },
+    BROWSERBACK,
+]
+
+TESTS = {
+    "auth_grant_fhir_calls": [
+        {"sequence": SEQ_AUTHORIZE_START},
+        CALL_LOGIN,
+        CLICK_AGREE_ACCESS,
+        {"sequence": SEQ_QUERY_FHIR_RESOURCES}
+    ],
+    "auth_deny_fhir_calls": [
+        {"sequence": SEQ_AUTHORIZE_START},
+        CALL_LOGIN,
+        CLICK_DENY_ACCESS,
+        CHECK_TESTCLIENT_START_PAGE
+    ],
+    "auth_grant_w_no_demo": [
+        {"sequence": SEQ_AUTHORIZE_START},
+        CALL_LOGIN,
+        CLICK_RADIO_NOT_SHARE,
+        CLICK_AGREE_ACCESS,
+        {"sequence": SEQ_QUERY_FHIR_RESOURCES_NO_DEMO}
+    ]
+}
+
+
+class SeleniumTests(TestCase):
+    '''
+    Test authorization and fhir flow through the built in testclient by
+    leveraging selenium web driver (chrome is used)
+    '''
+    wait_completed = False
+
+    def setUp(self):
+        super(SeleniumTests, self).setUp()
+        # a bit waiting for selenium service ready for sure
+        if not SeleniumTests.wait_completed:
+            time.sleep(20)
+            SeleniumTests.wait_completed = True
+            print("set wait_completed={}".format(SeleniumTests.wait_completed))
+        else:
+            print("wait_completed={}".format(SeleniumTests.wait_completed))
+
+        opt = webdriver.ChromeOptions()
+        # opt.add_argument('--headless')
+        opt.add_argument("--disable-dev-shm-usage")
+        opt.add_argument("--disable-web-security")
+        opt.add_argument("--allow-running-insecure-content")
+        opt.add_argument("--no-sandbox")
+        opt.add_argument("--disable-setuid-sandbox")
+        opt.add_argument("--disable-webgl")
+        opt.add_argument("--disable-popup-blocking")
+        opt.add_argument("--enable-javascript")
+        opt.add_argument('--allow-insecure-localhost')
+        opt.add_argument('--window-size=1920,1080')
+        opt.add_argument("--whitelisted-ips=''")
+
+        self.driver = webdriver.Remote(
+            command_executor='http://selenium-hub:4444',
+            desired_capabilities=DesiredCapabilities.CHROME, options=opt)
+
+        self.actions = {
+            Action.LOAD_PAGE: self._load_page,
+            Action.FIND_CLICK: self._find_and_click,
+            Action.FIND: self._find_and_return,
+            Action.FIND_SEND_KEY: self._find_and_sendkey,
+            Action.CHECK: self._check_page_title,
+            Action.CONTAIN_TEXT: self._check_page_content,
+            Action.GET_SAMPLE_TOKEN_START: self._click_get_sample_token,
+            Action.BACK: self._back,
+            Action.LOGIN: self._login,
+            Action.SLEEP: self._sleep,
+        }
+        self.use_mslsx = os.environ['USE_MSLSX']
+        self.login_seq = SEQ_LOGIN_MSLSX if self.use_mslsx == 'true' else SEQ_LOGIN_SLSX
+
+    def tearDown(self):
+        self.driver.quit()
+        super(SeleniumTests, self).tearDown()
+
+    def _find_and_click(self, timeout_sec, by, by_expr, **kwargs):
+        elem = WebDriverWait(self.driver, timeout_sec).until(EC.visibility_of_element_located((by, by_expr)))
+        self.assertIsNotNone(elem)
+        elem.click()
+        return elem
+
+    def _testclient_home(self, **kwargs):
+        return self._find_and_click(30, By.LINK_TEXT, LNK_TXT_RESTART_TESTCLIENT, **kwargs)
+
+    def _find_and_sendkey(self, timeout_sec, by, by_expr, txt, **kwargs):
+        elem = WebDriverWait(self.driver, timeout_sec).until(EC.visibility_of_element_located((by, by_expr)))
+        self.assertIsNotNone(elem)
+        elem.send_keys(txt)
+        return elem
+
+    def _click_get_sample_token(self, **kwargs):
+        return self._find_and_click(30, By.LINK_TEXT,
+                                    LNK_TXT_GET_TOKEN_V2 if kwargs.get("api_ver", API_V1) == API_V2 else LNK_TXT_GET_TOKEN_V1)
+
+    def _find_and_return(self, timeout_sec, by, by_expr, **kwargs):
+        elem = WebDriverWait(self.driver, timeout_sec).until(EC.visibility_of_element_located((by, by_expr)))
+        self.assertIsNotNone(elem)
+        return elem
+
+    def _load_page(self, url, **kwargs):
+        self.driver.get(url)
+
+    def _check_page_title(self, timeout_sec, by, by_expr, fmt, resource_type, **kwargs):
+        elem = self._find_and_return(timeout_sec, by, by_expr, **kwargs)
+        if not (elem.text == fmt.format(resource_type, kwargs.get("api_ver"))):
+            print("PAGE:{}".format(self.driver.page_source))
+        self.assertEqual(elem.text, fmt.format(resource_type, kwargs.get("api_ver")))
+
+    def _check_page_content(self, timeout_sec, by, by_expr, content_txt, **kwargs):
+        elem = self._find_and_return(timeout_sec, by, by_expr, **kwargs)
+        self.assertIn(content_txt, elem.text)
+
+    def _back(self, **kwargs):
+        self.driver.back()
+
+    def _sleep(self, sec, **kwargs):
+        time.sleep(sec)
+
+    def _login(self, step, **kwargs):
+        if self.use_mslsx == 'false':
+            # dismiss mymedicare popup if present
+            webdriver.ActionChains(self.driver).send_keys(Keys.ESCAPE).perform()
+        self._play(self.login_seq, step, **kwargs)
+
+    def _print_testcase_banner(self, test_name, api_ver, step_0, id_service, start=True):
+        print()
+        print("******************************************************************")
+        print(TESTCASE_BANNER_FMT.format("START" if start else "END", test_name, api_ver, step_0,
+                                         "MSLSX" if id_service == 'true' else "SLSX"))
+        print("******************************************************************")
+        print()
+
+    def _play(self, lst, step, **kwargs):
+        for s in lst:
+            seq = s.get("sequence")
+            # expects sequence of actions or action
+            if seq is not None:
+                self._play(seq, step, **kwargs)
+            else:
+                # single action
+                action = s.get('action', None)
+                step[0] = step[0] + 1
+                if action is not None:
+                    print("{}:{}:".format(step[0], s.get("display", "Not available")))
+                    if action == Action.LOGIN:
+                        self.actions[action](*s.get("params", []), step, **kwargs)
+                    else:
+                        self.actions[action](*s.get("params", []), **kwargs)
+                else:
+                    raise ValueError("Invalid test case, expect dict with action...")
+
+    def test_auth_grant_fhir_calls_v1(self):
+        step = [0]
+        test_name = "auth_grant_fhir_calls"
+        api_ver = API_V1
+        self._print_testcase_banner(test_name, api_ver, step[0], self.use_mslsx, True)
+        self._play(TESTS[test_name], step, api_ver=api_ver)
+        self._testclient_home()
+        self._print_testcase_banner(test_name, api_ver, step[0], self.use_mslsx, False)
+
+    def test_auth_grant_fhir_calls_v2(self):
+        step = [0]
+        test_name = "auth_grant_fhir_calls"
+        api_ver = API_V2
+        self._print_testcase_banner(test_name, api_ver, step[0], self.use_mslsx, True)
+        self._play(TESTS[test_name], step, api_ver=api_ver)
+        self._testclient_home()
+        self._print_testcase_banner(test_name, api_ver, step[0], self.use_mslsx, False)
+
+    def test_auth_deny_fhir_calls_v1(self):
+        step = [0]
+        test_name = "auth_deny_fhir_calls"
+        api_ver = API_V1
+        self._print_testcase_banner(test_name, api_ver, step[0], self.use_mslsx, True)
+        self._play(TESTS[test_name], step, api_ver=api_ver)
+        self._print_testcase_banner(test_name, api_ver, step[0], self.use_mslsx, False)
+
+    def test_auth_deny_fhir_calls_v2(self):
+        step = [0]
+        test_name = "auth_deny_fhir_calls"
+        api_ver = API_V2
+        self._print_testcase_banner(test_name, api_ver, step[0], self.use_mslsx, True)
+        self._play(TESTS[test_name], step, api_ver=api_ver)
+        self._print_testcase_banner(test_name, api_ver, step[0], self.use_mslsx, False)
+
+    def test_auth_grant_w_no_demo_v1(self):
+        step = [0]
+        test_name = "auth_grant_w_no_demo"
+        api_ver = API_V1
+        self._print_testcase_banner(test_name, api_ver, step[0], self.use_mslsx, True)
+        self._play(TESTS[test_name], step, api_ver=api_ver)
+        self._testclient_home()
+        self._print_testcase_banner(test_name, api_ver, step[0], self.use_mslsx, False)
+
+    def test_auth_grant_w_no_demo_v2(self):
+        step = [0]
+        test_name = "auth_grant_w_no_demo"
+        api_ver = API_V2
+        self._print_testcase_banner(test_name, api_ver, step[0], self.use_mslsx, True)
+        self._play(TESTS[test_name], step, api_ver=api_ver)
+        self._testclient_home()
+        self._print_testcase_banner(test_name, api_ver, step[0], self.use_mslsx, False)
diff --git a/docker-compose.selenium.yml b/docker-compose.selenium.yml
new file mode 100755
index 000000000..e66c670a2
--- /dev/null
+++ b/docker-compose.selenium.yml
@@ -0,0 +1,162 @@
+version: '3'
+
+services:
+  tests:
+    build:
+      context: ./
+      dockerfile: SeleniumDockerfile
+    command: python runtests.py apps.integration_tests.selenium_tests.SeleniumTests
+    environment:
+          - HOSTNAME_URL=${HOSTNAME_URL}
+          - USE_MSLSX=${USE_MSLSX}
+    volumes:
+      - .:/code
+    depends_on:
+      - bb2
+      - chrome
+
+  tests_w_slsx:
+    build:
+      context: ./
+      dockerfile: SeleniumDockerfile
+    command: python runtests.py apps.integration_tests.selenium_tests.SeleniumTests
+    environment:
+          - HOSTNAME_URL=${HOSTNAME_URL}
+          - USE_MSLSX=${USE_MSLSX}
+    volumes:
+      - .:/code
+    depends_on:
+      - bb2slsx
+      - chrome
+      
+  chrome:
+    image: selenium/node-chrome:4.0.0-beta-4-prerelease-20210517
+    volumes:
+      - /dev/shm:/dev/shm
+    depends_on:
+      - selenium-hub
+    environment:
+      - SE_EVENT_BUS_HOST=selenium-hub
+      - SE_EVENT_BUS_PUBLISH_PORT=4442
+      - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
+    ports:
+      - "6900:5900"
+    
+  # edge:
+  #   image: selenium/node-edge:4.0.0-beta-4-prerelease-20210517
+  #   volumes:
+  #     - /dev/shm:/dev/shm
+  #   depends_on:
+  #     - selenium-hub
+  #   environment:
+  #     - SE_EVENT_BUS_HOST=selenium-hub
+  #     - SE_EVENT_BUS_PUBLISH_PORT=4442
+  #     - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
+  #   ports:
+  #     - "6901:5900"
+    
+  # firefox:
+  #   image: selenium/node-firefox:4.0.0-beta-4-prerelease-20210517
+  #   volumes:
+  #     - /dev/shm:/dev/shm
+  #   depends_on:
+  #     - selenium-hub
+  #   environment:
+  #     - SE_EVENT_BUS_HOST=selenium-hub
+  #     - SE_EVENT_BUS_PUBLISH_PORT=4442
+  #     - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
+  #   ports:
+  #     - "6902:5900"
+    
+  selenium-hub:
+    image: selenium/hub:4.0.0-beta-4-prerelease-20210517
+    container_name: selenium-hub
+    ports:
+      - "4442:4442"
+      - "4443:4443"
+      - "4444:4444"
+
+  msls:
+    build:
+      context: ./dev-local/msls
+      dockerfile: Dockerfile
+    command: msls
+    ports:
+      - "8080:8080"
+
+  db:
+    image: postgres
+    environment:
+            - POSTGRES_DB=bluebutton
+            - POSTGRES_PASSWORD=toor
+    ports:
+            - "5432:5432"
+
+  bb2:
+    build: . 
+    command: ./docker-compose/bluebutton_server_start.sh '${DB_MIGRATIONS}' '${SUPER_USER_NAME}' '${SUPER_USER_EMAIL}' '${SUPER_USER_PASSWORD}' '${BB20_ENABLE_REMOTE_DEBUG}' '${BB20_REMOTE_DEBUG_WAIT_ATTACH}'
+    environment:
+            - DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE}
+            - DATABASES_CUSTOM=postgres://postgres:toor@db:5432/bluebutton
+            - OAUTHLIB_INSECURE_TRANSPORT=true
+            - DJANGO_DEFAULT_SAMPLE_FHIR_ID="-20140000008325"
+            - DJANGO_SECURE_SESSION=False
+            - FHIR_URL="https://prod-sbx.bfd.cms.gov"
+            - DJANGO_FHIR_CERTSTORE=/code/docker-compose/certstore/
+            - DJANGO_MEDICARE_SLSX_REDIRECT_URI=http://bb2:8000/mymedicare/sls-callback
+            - DJANGO_MEDICARE_SLSX_LOGIN_URI=http://msls:8080/sso/authorize?client_id=bb2api
+            - DJANGO_SLSX_HEALTH_CHECK_ENDPOINT=http://msls:8080/health
+            - DJANGO_SLSX_TOKEN_ENDPOINT=http://msls:8080/sso/session
+            - DJANGO_SLSX_SIGNOUT_ENDPOINT=http://msls:8080/sso/signout
+            - DJANGO_SLSX_USERINFO_ENDPOINT=http://msls:8080/v1/users
+            - DJANGO_SLSX_CLIENT_ID=bb2api
+            - DJANGO_SLSX_CLIENT_SECRET=${DJANGO_SLSX_CLIENT_SECRET}
+            - HOSTNAME_URL=${HOSTNAME_URL}
+            - DJANGO_SLSX_VERIFY_SSL_INTERNAL=False
+            - DJANGO_SLSX_VERIFY_SSL_EXTERNAL=True
+            - DJANGO_LOG_JSON_FORMAT_PRETTY=${DJANGO_LOG_JSON_FORMAT_PRETTY}
+            - DJANGO_USER_ID_ITERATIONS=${DJANGO_USER_ID_ITERATIONS}
+            - DJANGO_USER_ID_SALT=${DJANGO_USER_ID_SALT}
+    volumes:
+      - .:/code
+    ports:
+      - "8000:8000"
+      - "5678:5678"
+    depends_on:
+      - db
+      - msls
+
+  bb2slsx:
+    build: . 
+    command: ./docker-compose/bluebutton_server_start.sh '${DB_MIGRATIONS}' '${SUPER_USER_NAME}' '${SUPER_USER_EMAIL}' '${SUPER_USER_PASSWORD}' '${BB20_ENABLE_REMOTE_DEBUG}' '${BB20_REMOTE_DEBUG_WAIT_ATTACH}'
+    environment:
+            - DJANGO_SETTINGS_MODULE=hhs_oauth_server.settings.dev
+            - DATABASES_CUSTOM=postgres://postgres:toor@db:5432/bluebutton
+            - OAUTHLIB_INSECURE_TRANSPORT=true
+            - DJANGO_DEFAULT_SAMPLE_FHIR_ID="-20140000008325"
+            - DJANGO_SECURE_SESSION=False
+            - FHIR_URL="https://prod-sbx.bfd.cms.gov"
+            - DJANGO_FHIR_CERTSTORE=/code/docker-compose/certstore/
+            - DJANGO_SLSX_CLIENT_ID=bb2api
+            - DJANGO_SLSX_CLIENT_SECRET=${DJANGO_SLSX_CLIENT_SECRET}
+            - HOSTNAME_URL=${HOSTNAME_URL}
+            - DJANGO_MEDICARE_SLSX_REDIRECT_URI=http://bb2slsx:8000/mymedicare/sls-callback
+            - DJANGO_MEDICARE_SLSX_LOGIN_URI=https://test.medicare.gov/sso/authorize?client_id=bb2api
+            - DJANGO_SLSX_HEALTH_CHECK_ENDPOINT=https://test.accounts.cms.gov/health
+            - DJANGO_SLSX_TOKEN_ENDPOINT=https://test.medicare.gov/sso/session
+            - DJANGO_SLSX_SIGNOUT_ENDPOINT=https://test.medicare.gov/sso/signout
+            - DJANGO_SLSX_USERINFO_ENDPOINT=https://test.accounts.cms.gov/v1/users
+            # SSL verify for internal endpoints can't currently use SSL verification (this may change in the future)
+            - DJANGO_SLSX_VERIFY_SSL_INTERNAL=False
+            - DJANGO_SLSX_VERIFY_SSL_EXTERNAL=True
+            - DJANGO_LOG_JSON_FORMAT_PRETTY=True
+            - DJANGO_USER_ID_ITERATIONS=${DJANGO_USER_ID_ITERATIONS}
+            - DJANGO_USER_ID_SALT=${DJANGO_USER_ID_SALT}
+    volumes:
+      - .:/code
+    ports:
+      - "8000:8000"
+      - "5678:5678"
+    depends_on:
+      - db
+          
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
index 962614615..5d1d7f318 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -28,7 +28,7 @@ services:
             - DJANGO_FHIR_CERTSTORE=/code/docker-compose/certstore/
             # Commented out settings for MSLS:
             # - DJANGO_MEDICARE_SLSX_REDIRECT_URI=http://localhost:8000/mymedicare/sls-callback
-            # - DJANGO_MEDICARE_SLSX_LOGIN_URI=http://127.0.0.1:8080/sso/authorize?client_id=bb2api
+            # - DJANGO_MEDICARE_SLSX_LOGIN_URI=http://localhost:8080/sso/authorize?client_id=bb2api
             # - DJANGO_SLSX_HEALTH_CHECK_ENDPOINT=http://msls:8080/health
             # - DJANGO_SLSX_TOKEN_ENDPOINT=http://msls:8080/sso/session
             # - DJANGO_SLSX_SIGNOUT_ENDPOINT=http://msls:8080/sso/signout
diff --git a/docker-compose/bluebutton_server_start.sh b/docker-compose/bluebutton_server_start.sh
index 684331d32..4b005c28e 100755
--- a/docker-compose/bluebutton_server_start.sh
+++ b/docker-compose/bluebutton_server_start.sh
@@ -13,30 +13,26 @@ echo "SUPER_USER_EMAIL =" ${SUPER_USER_EMAIL}
 echo "SUPER_USER_PASSWORD =" ${SUPER_USER_PASSWORD}
 echo "BB20_ENABLE_REMOTE_DEBUG =" ${BB20_ENABLE_REMOTE_DEBUG}
 echo "BB20_REMOTE_DEBUG_WAIT_ATTACH =" ${BB20_REMOTE_DEBUG_WAIT_ATTACH}
+echo "DJANGO_USER_ID_ITERATIONS=" ${DJANGO_USER_ID_ITERATIONS}
+echo "DJANGO_USER_ID_SALT=" ${DJANGO_USER_ID_SALT}
+echo "DJANGO_SETTINGS_MODULE=" ${DJANGO_SETTINGS_MODULE}
 
-if [ -f ./migration.completed ]
-    echo "DB image migrations seems performed in container, skip..."
+if [ "${DB_MIGRATIONS}" = true ]
 then
-    if [ "${DB_MIGRATIONS}" = true ]
-    then
-        echo "DB_MIGRATIONS is true, run db image migration and models initialization."
-        python manage.py migrate
+    echo "run db image migration and models initialization."
+    python manage.py migrate
 
-        echo "from django.contrib.auth.models import User; User.objects.create_superuser('${SUPER_USER_NAME}', '${SUPER_USER_EMAIL}', '${SUPER_USER_PASSWORD}')" | python manage.py shell
-        python manage.py create_admin_groups
-        python manage.py loaddata scopes.json
-        python manage.py create_blue_button_scopes
-        python manage.py create_test_user_and_application
-        python manage.py create_user_identification_label_selection
-        python manage.py create_test_feature_switches
-    else
-        echo "DB_MIGRATIONS is false, no db image migration and models initialization will run here, you might need to manually run DB image migrations ..."
-    fi
-    # this is to prevent restart doing migration again
-    touch migration.completed
+    echo "from django.contrib.auth.models import User; User.objects.create_superuser('${SUPERUSER_NAME}', '${SUPERUSER_EMAIL}', '${SUPERUSER_PASSWORD}')" | python manage.py shell
+    python manage.py create_admin_groups
+    python manage.py loaddata scopes.json
+    python manage.py create_blue_button_scopes
+    python manage.py create_test_user_and_application
+    python manage.py create_user_identification_label_selection
+    python manage.py create_test_feature_switches
+else
+    echo "restarting blue button server, no db image migration and models initialization will run here, you might need to manually run DB image migrations."
 fi
 
-
 if [ ! -d 'bluebutton-css' ]
 then
     git clone https://github.com/CMSgov/bluebutton-css.git
diff --git a/docker-compose/readme.md b/docker-compose/readme.md
index 62ac6529f..5d31a0294 100644
--- a/docker-compose/readme.md
+++ b/docker-compose/readme.md
@@ -279,5 +279,25 @@ NOTES:
   * For the CBC related setup see these files for more details:
     * `Jenkinsfiles/Jenkinsfile.cbc-run-integration-tests` - Jenkinsfile for running the tests in a CBC project/job. 
     * `Jenkinsfiles/cbc-run-integration-tests.yaml` - Kubernetes docker container specification.  These settings will also need to be updated when there are CBC image naming changes.
-   
 
+## Developing and Running Selenium tests in Local Development
+You can run selenium tests by following below steps:
+
+  1. Make sure there is no blue button server and its dependent services running
+  2. Go to the base directory of the local repo and run:
+
+     use MSLSX (default)   
+     ```
+     ./docker-compose/run_selenium_tests_local_keybase.sh
+     ```
+
+     ```
+     ./docker-compose/run_selenium_tests_local_keybase.sh mslsx
+     ```
+
+     use SLSX
+     ```
+     ./docker-compose/run_selenium_tests_local_keybase.sh slsx
+     ```
+
+  3. To trouble shoot tests: point VNC client to localhost:6900
\ No newline at end of file
diff --git a/docker-compose/run_integration_tests_local_keybase.sh b/docker-compose/run_integration_tests_local_keybase.sh
index a262645b8..1e1882e41 100755
--- a/docker-compose/run_integration_tests_local_keybase.sh
+++ b/docker-compose/run_integration_tests_local_keybase.sh
@@ -160,6 +160,7 @@ else
   # Source ENVs
   source "${keybase_env}"
 
+
   # Check ENV vars have been sourced
   if [ -z "${DJANGO_USER_ID_SALT}" ]
   then
diff --git a/docker-compose/run_selenium_tests_local_keybase.sh b/docker-compose/run_selenium_tests_local_keybase.sh
new file mode 100755
index 000000000..4c18c2251
--- /dev/null
+++ b/docker-compose/run_selenium_tests_local_keybase.sh
@@ -0,0 +1,220 @@
+#!/bin/bash
+
+# Run the selenium tests in docker
+# 
+# NOTE:
+#
+#   1. You must be logged in to Keybase and have the BB2 team file system mounted.
+#
+#   2. You must also be connected to the VPN.
+#
+# SETTINGS:  You may need to customize these for your local setup.
+
+KEYBASE_ENV_FILE="team/bb20/infrastructure/creds/ENV_secrets_for_local_integration_tests.env"
+KEYBASE_CERTFILES_SUBPATH="team/bb20/infrastructure/certs/local_integration_tests/fhir_client/certstore/"
+
+DJANGO_FHIR_CERTSTORE="/certstore"
+CERTSTORE_TEMPORARY_MOUNT_PATH="/tmp/certstore"
+CERT_FILENAME="client_data_server_bluebutton_local_integration_tests_certificate.pem"
+KEY_FILENAME="client_data_server_bluebutton_local_integration_tests_private_key.pem"
+
+# BB2 service end point default
+HOSTNAME_URL="http://bb2:8000"
+
+# Backend FHIR server to use for selenium tests with FHIR requests:
+FHIR_URL="https://prod-sbx.bfd.cms.gov"
+
+# List of tests to run. To be passed in to runtests.py.
+TESTS_LIST="apps.integration_tests.selenium_tests.SeleniumTests"
+
+
+# Echo function that includes script name on each line for console log readability
+echo_msg () {
+  echo "$(basename $0): $*"
+}
+
+# main
+echo_msg
+echo_msg RUNNING SCRIPT:  ${0}
+echo_msg
+
+# Set bash builtins for safety
+set -e -u -o pipefail
+
+USE_MSLSX=true
+DOCKER_COMPOSE_SERVICE="tests"
+
+TEST_TYPE="--selenium"
+export DJANGO_SETTINGS_MODULE="hhs_oauth_server.settings.dev"
+
+# Parse command line option
+if [ $# -eq 0 ]
+then
+  echo "Use MSLSX for identity service."
+else
+  if [[ $1 != "slsx" && $1 != "mslsx" && $1 != "loggingit" ]]
+  then
+    echo
+    echo "COMMAND USAGE HELP"
+    echo "------------------"
+    echo
+    echo "  Use one of the following command line options for the type of test to run:"
+    echo
+    echo "    loggingit  = run integration tests for bb2 loggings, MSLSX used as identity service."
+    echo
+    echo "    slsx  = use SLSX for identity service."
+    echo
+    echo "    mslsx (default) = use MSLSX for identity service."
+    echo
+    exit 1
+  else
+    if [ $1 == "slsx" ]
+    then
+      USE_MSLSX=false
+      DOCKER_COMPOSE_SERVICE="tests_w_slsx"
+      HOSTNAME_URL="http://bb2slsx:8000"
+    fi
+    if [ $1 == "loggingit" ]
+    then
+      TEST_TYPE="--loggingit"
+      TESTS_LIST="apps.integration_tests.logging_tests.LoggingTests"
+      export DJANGO_SETTINGS_MODULE="hhs_oauth_server.settings.test_logging"
+      export DJANGO_LOG_JSON_FORMAT_PRETTY=False
+      mkdir -p docker-compose/tmp
+      if [ -f docker-compose/tmp/bb2_logging_test.log ]
+      then
+        rm -f docker-compose/tmp/bb2_logging_test.log
+      fi
+    fi
+  fi
+fi
+
+echo "DJANGO_SETTINGS_MODULE: " ${DJANGO_SETTINGS_MODULE}
+echo "DOCKER_COMPOSE_SERVICE: " ${DOCKER_COMPOSE_SERVICE}
+echo "HOSTNAME_URL: " ${HOSTNAME_URL}
+echo "TEST_TYPE: " ${TEST_TYPE}
+echo "TESTS: " ${TESTS_LIST}
+
+# Set KeyBase ENV path based on your type of system
+SYSTEM=$(uname -s)
+
+echo_msg " - Setting Keybase location based on SYSTEM type: ${SYSTEM}"
+echo_msg
+
+if [[ ${SYSTEM} == "Linux" ]]
+then
+  keybase_env_path="/keybase"
+elif [[ ${SYSTEM} == "Darwin" ]]
+then
+  keybase_env_path="/Volumes/keybase"
+else
+  # support cygwin
+  keybase_env_path="/cygdrive/k"
+  CERTSTORE_TEMPORARY_MOUNT_PATH="./docker-compose/certstore"
+  DJANGO_FHIR_CERTSTORE="/code/docker-compose/certstore"
+fi
+
+# Keybase ENV file
+keybase_env="${keybase_env_path}/${KEYBASE_ENV_FILE}"
+
+echo_msg " - Sourcing ENV secrets from: ${keybase_env}"
+echo_msg
+
+# Check that ENV file exists in correct location
+if [ ! -f "${keybase_env}" ]
+then
+  echo_msg
+  echo_msg "ERROR: The ENV secrets could NOT be found at: ${keybase_env}"
+  echo_msg
+  exit 1
+fi
+
+# Source ENVs
+source "${keybase_env}"
+
+# Check ENV vars have been sourced
+if [ -z "${DJANGO_USER_ID_SALT}" ]
+then
+  echo_msg "ERROR: The DJANGO_USER_ID_SALT variable was not sourced!"
+  exit 1
+fi
+if [ -z "${DJANGO_USER_ID_ITERATIONS}" ]
+then
+  echo_msg "ERROR: The DJANGO_USER_ID_ITERATIONS variable was not sourced!"
+  exit 1
+fi
+
+# Check temp certstore dir and create if not existing
+if [ -d "${CERTSTORE_TEMPORARY_MOUNT_PATH}" ]
+then
+  echo_msg
+  echo_msg "  - OK: The temporary certstore mount path is found at: ${CERTSTORE_TEMPORARY_MOUNT_PATH}"
+else
+  mkdir ${CERTSTORE_TEMPORARY_MOUNT_PATH}
+  echo_msg
+  echo_msg "  - OK: Created the temporary certstore mount path at: ${CERTSTORE_TEMPORARY_MOUNT_PATH}"
+fi
+
+# Keybase cert files
+keybase_certfiles="${keybase_env_path}/${KEYBASE_CERTFILES_SUBPATH}"
+keybase_cert_file="${keybase_certfiles}/${CERT_FILENAME}"
+keybase_key_file="${keybase_certfiles}/${KEY_FILENAME}"
+
+# Check that certfiles in keybase dir exist
+if [ -f "${keybase_cert_file}" ]
+then
+    echo_msg
+    echo_msg "  - OK: The cert file was found at: ${keybase_cert_file}"
+else
+    echo_msg
+    echo_msg "ERROR: The cert file could NOT be found at: ${keybase_cert_file}"
+    exit 1
+fi
+
+if [ -f ${keybase_key_file} ]
+then
+    echo_msg
+    echo_msg "  - OK: The key file was found at: ${keybase_key_file}"
+else
+    echo_msg
+    echo_msg "ERROR: The key file could NOT be found at: ${keybase_key_file}"
+    exit 1
+fi
+
+# Copy certfiles from KeyBase to local for container mount
+echo_msg "  - COPY certfiles from KeyBase to local temp for container mount..."
+echo_msg
+cp ${keybase_cert_file} "${CERTSTORE_TEMPORARY_MOUNT_PATH}/ca.cert.pem"
+cp ${keybase_key_file} "${CERTSTORE_TEMPORARY_MOUNT_PATH}/ca.key.nocrypt.pem"
+
+# stop all before run selenium tests
+docker-compose -f docker-compose.selenium.yml stop
+
+export DJANGO_PASSWORD_HASH_ITERATIONS=${DJANGO_PASSWORD_HASH_ITERATIONS}
+export DJANGO_USER_ID_SALT=${DJANGO_USER_ID_SALT}
+export DJANGO_USER_ID_ITERATIONS=${DJANGO_USER_ID_ITERATIONS}
+export DJANGO_SLSX_CLIENT_ID=${DJANGO_SLSX_CLIENT_ID}
+export DJANGO_SLSX_CLIENT_SECRET=${DJANGO_SLSX_CLIENT_SECRET}
+export HOSTNAME_URL=${HOSTNAME_URL}
+export USE_MSLSX=${USE_MSLSX}
+
+echo "Selenium tests ..."
+
+docker-compose -f docker-compose.selenium.yml run \
+${DOCKER_COMPOSE_SERVICE} bash -c "python runtests.py ${TEST_TYPE} ${TESTS_LIST}"
+
+# Remove certfiles from local directory
+echo_msg
+echo_msg Shred and Remove certfiles from CERTSTORE_TEMPORARY_MOUNT_PATH=${CERTSTORE_TEMPORARY_MOUNT_PATH}
+echo_msg
+
+if which shred
+then
+  echo_msg "  - Shredding files"
+  shred "${CERTSTORE_TEMPORARY_MOUNT_PATH}/ca.cert.pem"
+  shred "${CERTSTORE_TEMPORARY_MOUNT_PATH}/ca.key.nocrypt.pem"
+fi
+
+echo_msg "  - Removing files"
+rm "${CERTSTORE_TEMPORARY_MOUNT_PATH}/ca.cert.pem"
+rm "${CERTSTORE_TEMPORARY_MOUNT_PATH}/ca.key.nocrypt.pem"
diff --git a/hhs_oauth_server/settings/test_logging.py b/hhs_oauth_server/settings/test_logging.py
new file mode 100755
index 000000000..da344474a
--- /dev/null
+++ b/hhs_oauth_server/settings/test_logging.py
@@ -0,0 +1,70 @@
+from .test import *
+
+# Use env-specific logging config if present
+LOGGING = env("DJANGO_LOGGING", {
+    'version': 1,
+    'disable_existing_loggers': False,
+    'formatters': {
+        'verbose': {
+            'format': '%(asctime)s %(levelname)s '
+                      '[%(process)d] %(name)s line:%(lineno)d %(message)s'
+        },
+        'simple': {
+            'format': '%(asctime)s %(levelname)s %(name)s %(message)s'
+        },
+        'jsonout': {
+            'format': '{"env": "' + env('TARGET_ENV', 'DEV') + '", "time": "%(asctime)s", "level": "%(levelname)s", '
+                      '"name": "%(name)s", "message": "%(message)s"}',
+            'datefmt': '%Y-%m-%d %H:%M:%S'
+
+        }
+    },
+    'handlers': {
+        'console': {
+            'class': 'logging.StreamHandler',
+            'formatter': 'verbose',
+        },
+        'file': {
+            'class': 'logging.FileHandler',
+            'filename': '/code/docker-compose/tmp/bb2_logging_test.log',
+        },
+    },
+    'loggers': {
+        'hhs_server': {
+            'handlers': ['console', 'file'],
+            'level': 'INFO',
+        },
+        'hhs_oauth_server.accounts': {
+            'handlers': ['console'],
+            'level': 'INFO',
+        },
+        'oauth2_provider': {
+            'handlers': ['console'],
+            'level': 'INFO',
+        },
+        'oauthlib': {
+            'handlers': ['console'],
+            'level': 'INFO',
+        },
+        'unsuccessful_logins': {
+            'handlers': ['console'],
+            'level': 'INFO',
+        },
+        'admin_interface': {
+            'handlers': ['console'],
+            'level': 'INFO',
+        },
+        'tests': {
+            'handlers': ['console'],
+            'level': 'DEBUG',
+        },
+        'audit': {
+            'handlers': ['console', 'file'],
+            'level': 'INFO',
+        },
+        'performance': {
+            'handlers': ['console'],
+            'level': 'INFO',
+        }
+    },
+})
diff --git a/runtests.py b/runtests.py
index 5222fc6f3..f08ee411e 100644
--- a/runtests.py
+++ b/runtests.py
@@ -12,10 +12,18 @@
     Reference: https://docs.djangoproject.com/en/3.0/topics/testing/advanced/#defining-a-test-runner
 
     Command line arguments:
+
         --integration  This optional flag indicates tests are to run in integration test mode.
         Space separated list of Django tests to run.
 
+        --selenium  This optional flag indicates tests are to run in selenium test mode.
+        Space separated list of Django tests to run.
+
+        --loggingit  This optional flag indicates tests are to run in logging integration test mode.
+        Space separated list of Django tests to run.
+
     For example:
+
         $ docker-compose exec web python runtests.py apps.dot_ext.tests
 
         For more specific use:
@@ -34,6 +42,8 @@
 # Parse command line arguments
 parser = argparse.ArgumentParser()
 parser.add_argument('--integration', help='Integration tests mode', action='store_true')
+parser.add_argument('--selenium', help='Selenium tests mode', action='store_true')
+parser.add_argument('--loggingit', help='Logging tests mode (use selenium as driver)', action='store_true')
 parser.add_argument('test', nargs='*')
 args = parser.parse_args()
 
@@ -45,7 +55,8 @@
                     'DATABASES_CUSTOM', 'DJANGO_LOG_JSON_FORMAT_PRETTY']:
         if env_var in os.environ:
             del os.environ[env_var]
-else:
+elif not args.selenium and not args.loggingit:
+    # For tests using selenium, inherit SLSX config from context;
     # Unset ENV variables for Django unit type tests so default values get set.
     for env_var in ['FHIR_URL', 'DJANGO_MEDICARE_SLSX_LOGIN_URI', 'DJANGO_MEDICARE_SLSX_REDIRECT_URI',
                     'DJANGO_SLSX_USERINFO_ENDPOINT', 'DJANGO_SLSX_TOKEN_ENDPOINT',
@@ -56,7 +67,10 @@
             del os.environ[env_var]
 
 if __name__ == '__main__':
-    os.environ['DJANGO_SETTINGS_MODULE'] = 'hhs_oauth_server.settings.test'
+    if not args.loggingit:
+        os.environ['DJANGO_SETTINGS_MODULE'] = 'hhs_oauth_server.settings.test'
+    else:
+        os.environ['DJANGO_SETTINGS_MODULE'] = 'hhs_oauth_server.settings.test_logging'
     django.setup()
     TestRunner = get_runner(settings)
     test_runner = TestRunner()
diff --git a/templates/design_system/authorize_v2.html b/templates/design_system/authorize_v2.html
index b61980a50..b6b6d9097 100644
--- a/templates/design_system/authorize_v2.html
+++ b/templates/design_system/authorize_v2.html
@@ -70,7 +70,7 @@ 

Understand how your data is being used

- +
From e831bced19b9f1d4d267298a1b1a0799dff2709c Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Thu, 8 Jul 2021 11:30:50 -0700 Subject: [PATCH 02/39] adjust command line option name from --loggingit to --logit, meaning log integration tests. --- docker-compose/run_selenium_tests_local_keybase.sh | 8 ++++---- runtests.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docker-compose/run_selenium_tests_local_keybase.sh b/docker-compose/run_selenium_tests_local_keybase.sh index 4c18c2251..3a5efbcbf 100755 --- a/docker-compose/run_selenium_tests_local_keybase.sh +++ b/docker-compose/run_selenium_tests_local_keybase.sh @@ -52,7 +52,7 @@ if [ $# -eq 0 ] then echo "Use MSLSX for identity service." else - if [[ $1 != "slsx" && $1 != "mslsx" && $1 != "loggingit" ]] + if [[ $1 != "slsx" && $1 != "mslsx" && $1 != "logit" ]] then echo echo "COMMAND USAGE HELP" @@ -60,7 +60,7 @@ else echo echo " Use one of the following command line options for the type of test to run:" echo - echo " loggingit = run integration tests for bb2 loggings, MSLSX used as identity service." + echo " logit = run integration tests for bb2 loggings, MSLSX used as identity service." echo echo " slsx = use SLSX for identity service." echo @@ -74,9 +74,9 @@ else DOCKER_COMPOSE_SERVICE="tests_w_slsx" HOSTNAME_URL="http://bb2slsx:8000" fi - if [ $1 == "loggingit" ] + if [ $1 == "logit" ] then - TEST_TYPE="--loggingit" + TEST_TYPE="--logit" TESTS_LIST="apps.integration_tests.logging_tests.LoggingTests" export DJANGO_SETTINGS_MODULE="hhs_oauth_server.settings.test_logging" export DJANGO_LOG_JSON_FORMAT_PRETTY=False diff --git a/runtests.py b/runtests.py index f08ee411e..39e4a8122 100644 --- a/runtests.py +++ b/runtests.py @@ -19,7 +19,7 @@ --selenium This optional flag indicates tests are to run in selenium test mode. Space separated list of Django tests to run. - --loggingit This optional flag indicates tests are to run in logging integration test mode. + --logit This optional flag indicates tests are to run in logging integration test mode. Space separated list of Django tests to run. For example: @@ -43,7 +43,7 @@ parser = argparse.ArgumentParser() parser.add_argument('--integration', help='Integration tests mode', action='store_true') parser.add_argument('--selenium', help='Selenium tests mode', action='store_true') -parser.add_argument('--loggingit', help='Logging tests mode (use selenium as driver)', action='store_true') +parser.add_argument('--logit', help='Logging tests mode (use selenium as driver)', action='store_true') parser.add_argument('test', nargs='*') args = parser.parse_args() @@ -55,7 +55,7 @@ 'DATABASES_CUSTOM', 'DJANGO_LOG_JSON_FORMAT_PRETTY']: if env_var in os.environ: del os.environ[env_var] -elif not args.selenium and not args.loggingit: +elif not args.selenium and not args.logit: # For tests using selenium, inherit SLSX config from context; # Unset ENV variables for Django unit type tests so default values get set. for env_var in ['FHIR_URL', 'DJANGO_MEDICARE_SLSX_LOGIN_URI', 'DJANGO_MEDICARE_SLSX_REDIRECT_URI', @@ -67,7 +67,7 @@ del os.environ[env_var] if __name__ == '__main__': - if not args.loggingit: + if not args.logit: os.environ['DJANGO_SETTINGS_MODULE'] = 'hhs_oauth_server.settings.test' else: os.environ['DJANGO_SETTINGS_MODULE'] = 'hhs_oauth_server.settings.test_logging' From e10a888a78cac76abc07c8abb79e74428e40110d Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Thu, 8 Jul 2021 16:30:54 -0700 Subject: [PATCH 03/39] no echo for sensitive info. --- docker-compose/bluebutton_server_start.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/docker-compose/bluebutton_server_start.sh b/docker-compose/bluebutton_server_start.sh index 4b005c28e..6196d8616 100755 --- a/docker-compose/bluebutton_server_start.sh +++ b/docker-compose/bluebutton_server_start.sh @@ -13,8 +13,6 @@ echo "SUPER_USER_EMAIL =" ${SUPER_USER_EMAIL} echo "SUPER_USER_PASSWORD =" ${SUPER_USER_PASSWORD} echo "BB20_ENABLE_REMOTE_DEBUG =" ${BB20_ENABLE_REMOTE_DEBUG} echo "BB20_REMOTE_DEBUG_WAIT_ATTACH =" ${BB20_REMOTE_DEBUG_WAIT_ATTACH} -echo "DJANGO_USER_ID_ITERATIONS=" ${DJANGO_USER_ID_ITERATIONS} -echo "DJANGO_USER_ID_SALT=" ${DJANGO_USER_ID_SALT} echo "DJANGO_SETTINGS_MODULE=" ${DJANGO_SETTINGS_MODULE} if [ "${DB_MIGRATIONS}" = true ] From 751987cf2e52e79ddb12d5d9f9d4b4cfe3fcca08 Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Mon, 12 Jul 2021 11:53:45 -0700 Subject: [PATCH 04/39] code refactor. --- apps/integration_tests/common_utils.py | 13 + .../integration_test_fhir_resources.py | 49 +- apps/integration_tests/logging_tests.py | 431 +----------------- apps/integration_tests/selenium_cases.py | 359 +++++++++++++++ apps/integration_tests/selenium_tests.py | 369 +-------------- docker-compose/readme.md | 13 +- .../run_selenium_tests_local_keybase.sh | 2 +- 7 files changed, 425 insertions(+), 811 deletions(-) create mode 100755 apps/integration_tests/common_utils.py create mode 100755 apps/integration_tests/selenium_cases.py diff --git a/apps/integration_tests/common_utils.py b/apps/integration_tests/common_utils.py new file mode 100755 index 000000000..d23cc8a44 --- /dev/null +++ b/apps/integration_tests/common_utils.py @@ -0,0 +1,13 @@ +import jsonschema + +from jsonschema import validate + + +def validate_json_schema(schema, content): + try: + validate(instance=content, schema=schema) + except jsonschema.exceptions.ValidationError as e: + # Show error info for debugging + print("jsonschema.exceptions.ValidationError: ", e) + return False + return True diff --git a/apps/integration_tests/integration_test_fhir_resources.py b/apps/integration_tests/integration_test_fhir_resources.py index d87f0d122..733d320d2 100644 --- a/apps/integration_tests/integration_test_fhir_resources.py +++ b/apps/integration_tests/integration_test_fhir_resources.py @@ -1,15 +1,14 @@ import json -import jsonschema from django.conf import settings from django.contrib.staticfiles.testing import StaticLiveServerTestCase -from jsonschema import validate from oauth2_provider.models import AccessToken from rest_framework.test import APIClient from waffle.testutils import override_switch, override_flag from apps.test import BaseApiTest +from .common_utils import validate_json_schema from .endpoint_schemas import (COVERAGE_READ_SCHEMA_V2, EOB_READ_INPT_SCHEMA, FHIR_META_SCHEMA, @@ -89,15 +88,6 @@ def _setup_apiclient(self, client, fn=None, ln=None, fhir_id=None, hicn_hash=Non # Setup Bearer token: client.credentials(HTTP_AUTHORIZATION="Bearer " + at.token) - def _validateJsonSchema(self, schema, content): - try: - validate(instance=content, schema=schema) - except jsonschema.exceptions.ValidationError as e: - # Show error info for debugging - print("jsonschema.exceptions.ValidationError: ", e) - return False - return True - def _assertHasC4BBProfile(self, resource, c4bb_profile, v2=False): meta_profile = None try: @@ -161,7 +151,7 @@ def _stats_resource_by_type(self, bundle_json, cur_stats_dict, schema=None): if schema is not None: eob_profile = rs['meta']['profile'][0] self.assertIsNotNone(eob_profile) - self.assertTrue(self._validateJsonSchema(schema, rs)) + self.assertTrue(validate_json_schema(schema, rs)) is_matched = False @@ -235,7 +225,7 @@ def _call_userinfo_endpoint(self, v2=False): # Validate JSON Schema content = json.loads(response.content) # dump_content(json.dumps(content), "userinfo_{}.json".format('v2' if v2 else 'v1')) - self.assertEqual(self._validateJsonSchema(USERINFO_SCHEMA, content), True) + self.assertEqual(validate_json_schema(USERINFO_SCHEMA, content), True) @override_switch('require-scopes', active=True) def test_fhir_meta_endpoint(self): @@ -268,7 +258,7 @@ def _call_fhir_meta_endpoint(self, v2=False): self.assertIsNotNone(fhir_ver) self.assertEqual(fhir_ver, '4.0.0' if v2 else '3.0.2') # dump_content(json.dumps(content), "fhir_meta_{}.json".format('v2' if v2 else 'v1')) - self.assertEqual(self._validateJsonSchema(FHIR_META_SCHEMA, content), True) + self.assertEqual(validate_json_schema(FHIR_META_SCHEMA, content), True) @override_switch('require-scopes', active=True) def test_patient_endpoint(self): @@ -300,7 +290,7 @@ def _call_patient_endpoint(self, v2=False): content = json.loads(response.content) # dump_content(json.dumps(content), "patient_search_{}.json".format('v2' if v2 else 'v1')) # Validate JSON Schema - self.assertEqual(self._validateJsonSchema(PATIENT_SEARCH_SCHEMA, content), True) + self.assertEqual(validate_json_schema(PATIENT_SEARCH_SCHEMA, content), True) for r in content['entry']: resource = r['resource'] @@ -317,7 +307,7 @@ def _call_patient_endpoint(self, v2=False): # now v2 returns patient without identifier - think it's a bug, by-pass v2 assert to BB2 IT temporarily # until BFD resolve this. if not v2: - self.assertEqual(self._validateJsonSchema(PATIENT_READ_SCHEMA, content), True) + self.assertEqual(validate_json_schema(PATIENT_READ_SCHEMA, content), True) self._assertHasC4BBProfile(content, C4BB_PROFILE_URLS['PATIENT'], v2) # Assert there is no address lines and city in patient.address (BFD-379) @@ -357,7 +347,7 @@ def _call_coverage_endpoint(self, v2=False): content = json.loads(response.content) # dump_content(json.dumps(content), "coverage_search_{}.json".format('v2' if v2 else 'v1')) # Validate JSON Schema - self.assertEqual(self._validateJsonSchema(COVERAGE_SEARCH_SCHEMA, content), True) + self.assertEqual(validate_json_schema(COVERAGE_SEARCH_SCHEMA, content), True) # 3. Test READ VIEW endpoint response = client.get(self._get_fhir_url(FHIR_RES_TYPE_COVERAGE, "part-a-" + settings.DEFAULT_SAMPLE_FHIR_ID, v2)) @@ -366,7 +356,7 @@ def _call_coverage_endpoint(self, v2=False): # dump_content(json.dumps(content), "coverage_read_{}.json".format('v2' if v2 else 'v1')) # Validate JSON Schema self._assertHasC4BBProfile(content, C4BB_PROFILE_URLS['COVERAGE'], v2) - self.assertEqual(self._validateJsonSchema(COVERAGE_READ_SCHEMA_V2 if v2 else COVERAGE_READ_SCHEMA, content), True) + self.assertEqual(validate_json_schema(COVERAGE_READ_SCHEMA_V2 if v2 else COVERAGE_READ_SCHEMA, content), True) # 4. Test unauthorized READ request response = client.get(self._get_fhir_url(FHIR_RES_TYPE_COVERAGE, "part-a-99999999999999", v2)) @@ -401,7 +391,7 @@ def _call_coverage_search_endpoint(self, v2=False): # dump_content(json.dumps(content), "coverage_search_nav_p{}_{}.json".format(0, 'v2' if v2 else 'v1')) # Validate JSON Schema: bundle with entries and link section with page navigation: first, next, previous, last, self - self.assertEqual(self._validateJsonSchema(COVERAGE_SEARCH_SCHEMA, content), True) + self.assertEqual(validate_json_schema(COVERAGE_SEARCH_SCHEMA, content), True) total = content['total'] count = 10 @@ -472,7 +462,7 @@ def _call_eob_search_endpoint(self, v2=False): # dump_content(json.dumps(content), "eob_search_nav_p{}_{}.json".format(0, 'v2' if v2 else 'v1')) # Validate JSON Schema: bundle with entries and link section with page navigation: first, next, previous, last, self - self.assertEqual(self._validateJsonSchema(EOB_SEARCH_SCHEMA, content), True) + self.assertEqual(validate_json_schema(EOB_SEARCH_SCHEMA, content), True) total = content['total'] count = 10 @@ -483,9 +473,8 @@ def _call_eob_search_endpoint(self, v2=False): for i in range(page_total): lnk = content.get('link', None) - self.assertIsNotNone(lnk, - ("Field 'link' expected, " - "containing page navigation urls e.g. 'first', 'next', 'self', 'previous', 'last' ")) + self.assertIsNotNone(lnk, ("Field 'link' expected, " + "containing page navigation urls e.g. 'first', 'next', 'self', 'previous', 'last' ")) nav_info = self._extract_urls(lnk) if nav_info.get('next', None) is not None: response = client.get(nav_info['next']) @@ -524,7 +513,7 @@ def test_eob_profiles_endpoint(self): # Validate JSON Schema: bundle with entries and link section with page navigation: first, next, previous, last, self # comment out since the C4BB EOB resources do not validate with FHIR R4 json schema - self.assertEqual(self._validateJsonSchema(EOB_SEARCH_SCHEMA, content), True) + self.assertEqual(validate_json_schema(EOB_SEARCH_SCHEMA, content), True) total = content['total'] count = 10 @@ -587,7 +576,7 @@ def _call_eob_endpoint(self, v2=False): response = client.get(self._get_fhir_url(FHIR_RES_TYPE_EOB, None, v2)) self.assertEqual(response.status_code, 200) content = json.loads(response.content) - self.assertEqual(self._validateJsonSchema(EOB_SEARCH_SCHEMA, content), True) + self.assertEqual(validate_json_schema(EOB_SEARCH_SCHEMA, content), True) # dump_content(json.dumps(content), "eob_search_{}.json".format('v2' if v2 else 'v1')) # Validate JSON Schema for r in content['entry']: @@ -602,7 +591,7 @@ def _call_eob_endpoint(self, v2=False): self.assertEqual(response.status_code, 200) if not v2: # Validate JSON Schema - self.assertEqual(self._validateJsonSchema(EOB_READ_SCHEMA, content), True) + self.assertEqual(validate_json_schema(EOB_READ_SCHEMA, content), True) self._assertHasC4BBProfile(content, C4BB_PROFILE_URLS['NONCLINICIAN'], v2) # 4. Test SEARCH VIEW endpoint v1 and v2 (BB2-418 EOB V2 PDE profile) @@ -611,7 +600,7 @@ def _call_eob_endpoint(self, v2=False): # Validate JSON Schema content = json.loads(response.content) # dump_content(json.dumps(content), "eob_search_pt_{}.json".format('v2' if v2 else 'v1')) - self.assertEqual(self._validateJsonSchema(EOB_SEARCH_SCHEMA, content), True) + self.assertEqual(validate_json_schema(EOB_SEARCH_SCHEMA, content), True) for r in content['entry']: self._assertHasC4BBProfile(r['resource'], C4BB_PROFILE_URLS['NONCLINICIAN'], v2) @@ -646,7 +635,7 @@ def _call_eob_endpoint_pde(self, v2=False): self.assertEqual(response.status_code, 200) if not v2: # Validate JSON Schema for v1 - self.assertEqual(self._validateJsonSchema(EOB_READ_INPT_SCHEMA, content), True) + self.assertEqual(validate_json_schema(EOB_READ_INPT_SCHEMA, content), True) self._assertHasC4BBProfile(content, C4BB_PROFILE_URLS['PHARMACY'], v2) @override_switch('require-scopes', active=True) @@ -669,7 +658,7 @@ def _call_eob_endpoint_inpatient(self, v2=False): self.assertEqual(response.status_code, 200) if not v2: # Validate JSON Schema v1 - self.assertEqual(self._validateJsonSchema(EOB_READ_INPT_SCHEMA, content), True) + self.assertEqual(validate_json_schema(EOB_READ_INPT_SCHEMA, content), True) self._assertHasC4BBProfile(content, C4BB_PROFILE_URLS['INPATIENT'], v2) @override_switch('require-scopes', active=True) @@ -692,7 +681,7 @@ def _call_eob_endpoint_outpatient(self, v2=False): self.assertEqual(response.status_code, 200) if not v2: # Validate JSON Schema v1 - self.assertEqual(self._validateJsonSchema(EOB_READ_INPT_SCHEMA, content), True) + self.assertEqual(validate_json_schema(EOB_READ_INPT_SCHEMA, content), True) else: self.assertEqual(response.status_code, 200) self._assertHasC4BBProfile(content, C4BB_PROFILE_URLS['OUTPATIENT'], v2) diff --git a/apps/integration_tests/logging_tests.py b/apps/integration_tests/logging_tests.py index cedcbcc79..65d662af7 100755 --- a/apps/integration_tests/logging_tests.py +++ b/apps/integration_tests/logging_tests.py @@ -1,22 +1,11 @@ import copy import json -import jsonschema -import os import re -import time -from django.conf import settings -from django.test import TestCase -from enum import Enum from json.decoder import JSONDecodeError -from jsonschema import validate -from selenium import webdriver -from selenium.webdriver.common.by import By -from selenium.webdriver.support import expected_conditions as EC -from selenium.webdriver.support.wait import WebDriverWait -from selenium.webdriver.common.desired_capabilities import DesiredCapabilities -from selenium.webdriver.common.keys import Keys +from apps.integration_tests.common_utils import validate_json_schema +from apps.integration_tests.selenium_tests import SeleniumTests from apps.integration_tests.log_event_schemas import ( LOG_MIDDLEWARE_FHIR_READ_EVENT_SCHEMA, LOG_MIDDLEWARE_FHIR_SEARCH_EVENT_SCHEMA, @@ -171,419 +160,12 @@ ] -TESTCLIENT_BUNDLE_LABEL_FMT = "Response (Bundle of {}), API version: {}" -TESTCLIENT_RESOURCE_LABEL_FMT = "Response ({}), API version: {}" -MESSAGE_NO_PERMISSION = "You do not have permission to perform this action." -TESTCASE_BANNER_FMT = "** {} TEST: {}, API: {}, STEP: {}, {}" -''' -UI Widget text: texts on e.g. buttons, links, labels etc. -''' -LNK_TXT_TESTCLIENT = "Test Client" -LNK_TXT_GET_TOKEN_V1 = "Get a Sample Authorization Token" -LNK_TXT_GET_TOKEN_V2 = "Get a Sample Authorization Token for v2" -LNK_TXT_AUTH_AS_BENE = "Authorize as a Beneficiary" -LNK_TXT_RESTART_TESTCLIENT = "restart testclient" -# FHIR search result bundle pagination -LNK_TXT_NAV_FIRST = "first" -LNK_TXT_NAV_NEXT = "next" -LNK_TXT_NAV_PREV = "previous" -LNK_TXT_NAV_LAST = "last" -LNK_TXT_NAV_SELF = "self" -# FHIR resources query page -LNK_TXT_PATIENT = "Patient" -LNK_TXT_EOB = "ExplanationOfBenefit" -LNK_TXT_COVERAGE = "Coverage" -LNK_TXT_PROFILE = "Profile" -LNK_TXT_METADATA = "FHIR Metadata" -LNK_TXT_OIDC_DISCOVERY = "OIDC Discovery" -# FHIR result page label H2 -LAB_FHIR_RESULTPAGE_H2 = "h2" -CONTENT_FHIR_RESULTPAGE_PRE = "pre" -# MSLSX login form -TXT_FLD_SUB_MSLSX = "username" -TXT_FLD_HICN_MSLSX = "hicn" -TXT_FLD_MBI_MSLSX = "mbi" -TXT_FLD_VAL_SUB_MSLSX = "fred" -MSLSX_TXT_FLD_HICN_VAL = "1000044680" -MSLSX_TXT_FLD_MBI_VAL = "2SW4N00AA00" -MSLSX_CSS_BUTTON = "button" -# SLSX login form -SLSX_TXT_FLD_USERNAME = "username-textbox" -SLSX_TXT_FLD_PASSWORD = "password-textbox" -SLSX_TXT_FLD_USERNAME_VAL = "BBUser00000" -SLSX_TXT_FLD_PASSWORD_VAL = "PW00000!" -SLSX_CSS_BUTTON = "login-button" -# Demographic info access grant form -BTN_ID_GRANT_DEMO_ACCESS = "approve" -BTN_ID_DENY_DEMO_ACCESS = "deny" -BTN_ID_RADIO_NOT_SHARE = "label:nth-child(5)" -# API versions -API_V2 = "v2" -API_V1 = "v1" - -class Action(Enum): - LOAD_PAGE = 1 - FIND_CLICK = 2 - FIND = 3 - FIND_SEND_KEY = 4 - CHECK = 5 - BACK = 6 - LOGIN = 7 - CONTAIN_TEXT = 8 - GET_SAMPLE_TOKEN_START = 9 - SLEEP = 10 - - -BROWSERBACK = { - "display": "Back to FHIR resource page", - "action": Action.BACK, -} - -WAIT_SECONDS = { - "display": "Sleep seconds...", - "action": Action.SLEEP, - "params": [3], -} - -CHECK_TESTCLIENT_START_PAGE = { - "display": "Check it's on 'Test Client' start page", - "action": Action.FIND, - "params": [30, By.LINK_TEXT, LNK_TXT_GET_TOKEN_V1] -} - -CLICK_TESTCLIENT = { - "display": "Click link 'Test Client'", - "action": Action.FIND_CLICK, - "params": [30, By.LINK_TEXT, LNK_TXT_TESTCLIENT] -} - -CLICK_RADIO_NOT_SHARE = { - "display": "Click 'Share healthcare data, but not your personal info' on DEMO info grant form", - "action": Action.FIND_CLICK, - "params": [20, By.CSS_SELECTOR, BTN_ID_RADIO_NOT_SHARE] -} - -CLICK_AGREE_ACCESS = { - "display": "Click 'Agree' on DEMO info grant form", - "action": Action.FIND_CLICK, - "params": [20, By.ID, BTN_ID_GRANT_DEMO_ACCESS] -} - -CLICK_DENY_ACCESS = { - "display": "Click 'Deny' on DEMO info grant form", - "action": Action.FIND_CLICK, - "params": [20, By.ID, BTN_ID_DENY_DEMO_ACCESS] -} - -CALL_LOGIN = { - "display": "Start login ...", - "action": Action.LOGIN, -} - -SEQ_LOGIN_MSLSX = [ - { - "display": "Input SUB(username)", - "action": Action.FIND_SEND_KEY, - "params": [20, By.NAME, TXT_FLD_SUB_MSLSX, TXT_FLD_VAL_SUB_MSLSX] - }, - { - "display": "Input hicn", - "action": Action.FIND_SEND_KEY, - "params": [20, By.NAME, TXT_FLD_HICN_MSLSX, MSLSX_TXT_FLD_HICN_VAL] - }, - { - "display": "Input mbi", - "action": Action.FIND_SEND_KEY, - "params": [20, By.NAME, TXT_FLD_MBI_MSLSX, MSLSX_TXT_FLD_MBI_VAL] - }, - { - "display": "Click 'submit' on MSLSX login form", - "action": Action.FIND_CLICK, - "params": [20, By.CSS_SELECTOR, MSLSX_CSS_BUTTON] - }, -] - -SEQ_LOGIN_SLSX = [ - { - "display": "MyMedicare login username", - "action": Action.FIND_SEND_KEY, - "params": [20, By.ID, SLSX_TXT_FLD_USERNAME, SLSX_TXT_FLD_USERNAME_VAL] - }, - { - "display": "MyMedicare login password", - "action": Action.FIND_SEND_KEY, - "params": [20, By.ID, SLSX_TXT_FLD_PASSWORD, SLSX_TXT_FLD_PASSWORD_VAL] - }, - { - "display": "Click 'submit' on SLSX login form", - "action": Action.FIND_CLICK, - "params": [20, By.ID, SLSX_CSS_BUTTON] - }, -] - -SEQ_AUTHORIZE_START = [ - { - "display": "Load BB2 Landing Page ...", - "action": Action.LOAD_PAGE, - "params": [settings.HOSTNAME_URL] - }, - CLICK_TESTCLIENT, - { - "display": "Click link to get sample token v1/v2", - "action": Action.GET_SAMPLE_TOKEN_START, - }, - { - "display": "Click link 'Authorize as a Beneficiary' - start authorization", - "action": Action.FIND_CLICK, - "params": [30, By.LINK_TEXT, LNK_TXT_AUTH_AS_BENE] - }, -] - -SEQ_QUERY_FHIR_RESOURCES = [ - { - "display": "Click 'Patient' on FHIR resources page", - "action": Action.FIND_CLICK, - "params": [20, By.LINK_TEXT, LNK_TXT_PATIENT] - }, - { - "display": "Check Patient result page title", - "action": Action.CHECK, - "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, LNK_TXT_PATIENT] - }, - BROWSERBACK, - { - "display": "Click 'Coverage' on FHIR resources page", - "action": Action.FIND_CLICK, - "params": [20, By.LINK_TEXT, LNK_TXT_COVERAGE] - }, - { - "display": "Check Coverage result page title", - "action": Action.CHECK, - "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_BUNDLE_LABEL_FMT, LNK_TXT_COVERAGE] - }, - { - "display": "Check and click Coverage result page navigation links 'last'", - "action": Action.FIND_CLICK, - "params": [20, By.LINK_TEXT, LNK_TXT_NAV_LAST] - }, - CLICK_TESTCLIENT, - { - "display": "Click 'ExplanationOfBenefit' on FHIR resources page", - "action": Action.FIND_CLICK, - "params": [20, By.LINK_TEXT, LNK_TXT_EOB] - }, - { - "display": "Check ExplanationOfBenefit result page title", - "action": Action.CHECK, - "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_BUNDLE_LABEL_FMT, LNK_TXT_EOB] - }, - { - "display": "Check and click ExplanationOfBenefit result page navigation links 'last'", - "action": Action.FIND_CLICK, - "params": [20, By.LINK_TEXT, LNK_TXT_NAV_LAST] - }, - WAIT_SECONDS, - CLICK_TESTCLIENT, - WAIT_SECONDS, - { - "display": "Click 'Profile' on FHIR resources page", - "action": Action.FIND_CLICK, - "params": [20, By.LINK_TEXT, LNK_TXT_PROFILE] - }, - WAIT_SECONDS, - { - "display": "Check Profile result page title", - "action": Action.CHECK, - "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, - "{} (OIDC Userinfo)".format(LNK_TXT_PROFILE)] - }, - BROWSERBACK, - { - "display": "Click 'FHIR Metadata' on FHIR resources page", - "action": Action.FIND_CLICK, - "params": [20, By.LINK_TEXT, LNK_TXT_METADATA] - }, - { - "display": "Check FHIR Metadata result page title", - "action": Action.CHECK, - "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, LNK_TXT_METADATA] - }, - BROWSERBACK, - { - "display": "Click 'OIDC Discovery' on FHIR resources page", - "action": Action.FIND_CLICK, - "params": [20, By.LINK_TEXT, LNK_TXT_OIDC_DISCOVERY] - }, - { - "display": "Check OIDC Discovery result page title", - "action": Action.CHECK, - "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, LNK_TXT_OIDC_DISCOVERY] - }, - BROWSERBACK, -] - -TESTS = { - "auth_fhir_flow_calls_logging": [ - {"sequence": SEQ_AUTHORIZE_START}, - CALL_LOGIN, - CLICK_AGREE_ACCESS, - {"sequence": SEQ_QUERY_FHIR_RESOURCES} - ], -} - - -class LoggingTests(TestCase): +class LoggingTests(SeleniumTests): ''' Test loggings generated from authorization and fhir flow using the built in testclient as the driver (selenium) ''' - wait_completed = False - - def _validateJsonSchema(self, schema, content): - # print("SCHEMA={}".format(schema)) - # print("CONTENT={}".format(content)) - try: - validate(instance=content, schema=schema) - except jsonschema.exceptions.ValidationError as e: - # Show error info for debugging - print("jsonschema.exceptions.ValidationError: ", e) - return False - return True - - def setUp(self): - super(LoggingTests, self).setUp() - # a bit waiting for selenium service ready for sure - if not LoggingTests.wait_completed: - time.sleep(20) - LoggingTests.wait_completed = True - print("set wait_completed={}".format(LoggingTests.wait_completed)) - else: - print("wait_completed={}".format(LoggingTests.wait_completed)) - - opt = webdriver.ChromeOptions() - opt.add_argument('--headless') - opt.add_argument("--disable-dev-shm-usage") - opt.add_argument("--disable-web-security") - opt.add_argument("--allow-running-insecure-content") - opt.add_argument("--no-sandbox") - opt.add_argument("--disable-setuid-sandbox") - opt.add_argument("--disable-webgl") - opt.add_argument("--disable-popup-blocking") - opt.add_argument("--enable-javascript") - opt.add_argument('--allow-insecure-localhost') - opt.add_argument('--window-size=1920,1080') - opt.add_argument("--whitelisted-ips=''") - - self.driver = webdriver.Remote( - command_executor='http://selenium-hub:4444', - desired_capabilities=DesiredCapabilities.CHROME, options=opt) - - self.actions = { - Action.LOAD_PAGE: self._load_page, - Action.FIND_CLICK: self._find_and_click, - Action.FIND: self._find_and_return, - Action.FIND_SEND_KEY: self._find_and_sendkey, - Action.CHECK: self._check_page_title, - Action.CONTAIN_TEXT: self._check_page_content, - Action.GET_SAMPLE_TOKEN_START: self._click_get_sample_token, - Action.BACK: self._back, - Action.LOGIN: self._login, - Action.SLEEP: self._sleep, - } - self.use_mslsx = os.environ['USE_MSLSX'] - self.login_seq = SEQ_LOGIN_MSLSX if self.use_mslsx == 'true' else SEQ_LOGIN_SLSX - - def tearDown(self): - self.driver.quit() - super(LoggingTests, self).tearDown() - - def _find_and_click(self, timeout_sec, by, by_expr, **kwargs): - elem = WebDriverWait(self.driver, timeout_sec).until(EC.visibility_of_element_located((by, by_expr))) - self.assertIsNotNone(elem) - elem.click() - return elem - - def _testclient_home(self, **kwargs): - return self._find_and_click(30, By.LINK_TEXT, LNK_TXT_RESTART_TESTCLIENT, **kwargs) - - def _find_and_sendkey(self, timeout_sec, by, by_expr, txt, **kwargs): - elem = WebDriverWait(self.driver, timeout_sec).until(EC.visibility_of_element_located((by, by_expr))) - self.assertIsNotNone(elem) - elem.send_keys(txt) - return elem - - def _click_get_sample_token(self, **kwargs): - return self._find_and_click(30, By.LINK_TEXT, - LNK_TXT_GET_TOKEN_V2 if kwargs.get("api_ver", API_V1) == API_V2 else LNK_TXT_GET_TOKEN_V1) - - def _find_and_return(self, timeout_sec, by, by_expr, **kwargs): - elem = WebDriverWait(self.driver, timeout_sec).until(EC.visibility_of_element_located((by, by_expr))) - self.assertIsNotNone(elem) - return elem - - def _load_page(self, url, **kwargs): - self.driver.get(url) - - def _check_page_title(self, timeout_sec, by, by_expr, fmt, resource_type, **kwargs): - elem = self._find_and_return(timeout_sec, by, by_expr, **kwargs) - if not (elem.text == fmt.format(resource_type, kwargs.get("api_ver"))): - print("PAGE:{}".format(self.driver.page_source)) - self.assertEqual(elem.text, fmt.format(resource_type, kwargs.get("api_ver"))) - - def _check_page_content(self, timeout_sec, by, by_expr, content_txt, **kwargs): - elem = self._find_and_return(timeout_sec, by, by_expr, **kwargs) - self.assertIn(content_txt, elem.text) - - def _back(self, **kwargs): - self.driver.back() - - def _sleep(self, sec, **kwargs): - time.sleep(sec) - - def _login(self, step, **kwargs): - if self.use_mslsx == 'false': - # dismiss mymedicare popup if present - webdriver.ActionChains(self.driver).send_keys(Keys.ESCAPE).perform() - self._play(self.login_seq, step, **kwargs) - - def _print_testcase_banner(self, test_name, api_ver, step_0, id_service, start=True): - print() - print("******************************************************************") - print(TESTCASE_BANNER_FMT.format("START" if start else "END", test_name, api_ver, step_0, - "MSLSX" if id_service == 'true' else "SLSX")) - print("******************************************************************") - print() - - def _play(self, lst, step, **kwargs): - for s in lst: - seq = s.get("sequence") - # expects sequence of actions or action - if seq is not None: - self._play(seq, step, **kwargs) - else: - # single action - action = s.get('action', None) - step[0] = step[0] + 1 - if action is not None: - print("{}:{}:".format(step[0], s.get("display", "Not available"))) - if action == Action.LOGIN: - self.actions[action](*s.get("params", []), step, **kwargs) - else: - self.actions[action](*s.get("params", []), **kwargs) - else: - raise ValueError("Invalid test case, expect dict with action...") - - def test_auth_fhir_flows_logging(self): - step = [0] - test_name = "auth_fhir_flow_calls_logging" - api_ver = API_V1 - self._print_testcase_banner(test_name, api_ver, step[0], self.use_mslsx, True) - self._play(TESTS[test_name], step, api_ver=api_ver) - self._testclient_home() - self._validate_events() - self._print_testcase_banner(test_name, api_ver, step[0], self.use_mslsx, False) - def _validate_events(self): # validate middleware logging records in log file ./docker-compose/tmp/bb2_logging_test.log with open(TEST_LOGGING_FILE, 'r') as f: @@ -605,10 +187,15 @@ def _validate_events(self): self.assertTrue(re.match(event_desc.get('path_regex'), p)) else: self.assertEqual(p, event_desc.get('path')) - self.assertTrue(self._validateJsonSchema(event_desc.get('schema'), event_json)) + self.assertTrue(validate_json_schema(event_desc.get('schema'), event_json)) except JSONDecodeError: # skip non json line pass # all log events present and validated self.assertEqual(len(expected_events), 0) + + def test_auth_fhir_flows_logging(self): + self.test_auth_grant_fhir_calls_v1() + print("validating logging events in log...") + self._validate_events() diff --git a/apps/integration_tests/selenium_cases.py b/apps/integration_tests/selenium_cases.py new file mode 100755 index 000000000..34fc68773 --- /dev/null +++ b/apps/integration_tests/selenium_cases.py @@ -0,0 +1,359 @@ +from django.conf import settings +from enum import Enum +from selenium.webdriver.common.by import By + + +class Action(Enum): + LOAD_PAGE = 1 + FIND_CLICK = 2 + FIND = 3 + FIND_SEND_KEY = 4 + CHECK = 5 + BACK = 6 + LOGIN = 7 + CONTAIN_TEXT = 8 + GET_SAMPLE_TOKEN_START = 9 + SLEEP = 10 + + +TESTCLIENT_BUNDLE_LABEL_FMT = "Response (Bundle of {}), API version: {}" +TESTCLIENT_RESOURCE_LABEL_FMT = "Response ({}), API version: {}" +MESSAGE_NO_PERMISSION = "You do not have permission to perform this action." +TESTCASE_BANNER_FMT = "** {} TEST: {}, API: {}, STEP: {}, {}" +''' +UI Widget text: texts on e.g. buttons, links, labels etc. +''' +LNK_TXT_TESTCLIENT = "Test Client" +LNK_TXT_GET_TOKEN_V1 = "Get a Sample Authorization Token" +LNK_TXT_GET_TOKEN_V2 = "Get a Sample Authorization Token for v2" +LNK_TXT_AUTH_AS_BENE = "Authorize as a Beneficiary" +LNK_TXT_RESTART_TESTCLIENT = "restart testclient" +# FHIR search result bundle pagination +LNK_TXT_NAV_FIRST = "first" +LNK_TXT_NAV_NEXT = "next" +LNK_TXT_NAV_PREV = "previous" +LNK_TXT_NAV_LAST = "last" +LNK_TXT_NAV_SELF = "self" +# FHIR resources query page +LNK_TXT_PATIENT = "Patient" +LNK_TXT_EOB = "ExplanationOfBenefit" +LNK_TXT_COVERAGE = "Coverage" +LNK_TXT_PROFILE = "Profile" +LNK_TXT_METADATA = "FHIR Metadata" +LNK_TXT_OIDC_DISCOVERY = "OIDC Discovery" +# FHIR result page label H2 +LAB_FHIR_RESULTPAGE_H2 = "h2" +CONTENT_FHIR_RESULTPAGE_PRE = "pre" +# MSLSX login form +TXT_FLD_SUB_MSLSX = "username" +TXT_FLD_HICN_MSLSX = "hicn" +TXT_FLD_MBI_MSLSX = "mbi" +TXT_FLD_VAL_SUB_MSLSX = "fred" +MSLSX_TXT_FLD_HICN_VAL = "1000044680" +MSLSX_TXT_FLD_MBI_VAL = "2SW4N00AA00" +MSLSX_CSS_BUTTON = "button" +# SLSX login form +SLSX_TXT_FLD_USERNAME = "username-textbox" +SLSX_TXT_FLD_PASSWORD = "password-textbox" +SLSX_TXT_FLD_USERNAME_VAL = "BBUser00000" +SLSX_TXT_FLD_PASSWORD_VAL = "PW00000!" +SLSX_CSS_BUTTON = "login-button" +# Demographic info access grant form +BTN_ID_GRANT_DEMO_ACCESS = "approve" +BTN_ID_DENY_DEMO_ACCESS = "deny" +BTN_ID_RADIO_NOT_SHARE = "label:nth-child(5)" +# API versions +API_V2 = "v2" +API_V1 = "v1" + +BROWSERBACK = { + "display": "Back to FHIR resource page", + "action": Action.BACK, +} + +WAIT_SECONDS = { + "display": "Sleep seconds...", + "action": Action.SLEEP, + "params": [3], +} + +CHECK_TESTCLIENT_START_PAGE = { + "display": "Check it's on 'Test Client' start page", + "action": Action.FIND, + "params": [30, By.LINK_TEXT, LNK_TXT_GET_TOKEN_V1] +} + +CLICK_TESTCLIENT = { + "display": "Click link 'Test Client'", + "action": Action.FIND_CLICK, + "params": [30, By.LINK_TEXT, LNK_TXT_TESTCLIENT] +} + +CLICK_RADIO_NOT_SHARE = { + "display": "Click 'Share healthcare data, but not your personal info' on DEMO info grant form", + "action": Action.FIND_CLICK, + "params": [20, By.CSS_SELECTOR, BTN_ID_RADIO_NOT_SHARE] +} + +CLICK_AGREE_ACCESS = { + "display": "Click 'Agree' on DEMO info grant form", + "action": Action.FIND_CLICK, + "params": [20, By.ID, BTN_ID_GRANT_DEMO_ACCESS] +} + +CLICK_DENY_ACCESS = { + "display": "Click 'Deny' on DEMO info grant form", + "action": Action.FIND_CLICK, + "params": [20, By.ID, BTN_ID_DENY_DEMO_ACCESS] +} + +CALL_LOGIN = { + "display": "Start login ...", + "action": Action.LOGIN, +} + +SEQ_LOGIN_MSLSX = [ + { + "display": "Input SUB(username)", + "action": Action.FIND_SEND_KEY, + "params": [20, By.NAME, TXT_FLD_SUB_MSLSX, TXT_FLD_VAL_SUB_MSLSX] + }, + { + "display": "Input hicn", + "action": Action.FIND_SEND_KEY, + "params": [20, By.NAME, TXT_FLD_HICN_MSLSX, MSLSX_TXT_FLD_HICN_VAL] + }, + { + "display": "Input mbi", + "action": Action.FIND_SEND_KEY, + "params": [20, By.NAME, TXT_FLD_MBI_MSLSX, MSLSX_TXT_FLD_MBI_VAL] + }, + { + "display": "Click 'submit' on MSLSX login form", + "action": Action.FIND_CLICK, + "params": [20, By.CSS_SELECTOR, MSLSX_CSS_BUTTON] + }, +] + +SEQ_LOGIN_SLSX = [ + { + "display": "MyMedicare login username", + "action": Action.FIND_SEND_KEY, + "params": [20, By.ID, SLSX_TXT_FLD_USERNAME, SLSX_TXT_FLD_USERNAME_VAL] + }, + { + "display": "MyMedicare login password", + "action": Action.FIND_SEND_KEY, + "params": [20, By.ID, SLSX_TXT_FLD_PASSWORD, SLSX_TXT_FLD_PASSWORD_VAL] + }, + { + "display": "Click 'submit' on SLSX login form", + "action": Action.FIND_CLICK, + "params": [20, By.ID, SLSX_CSS_BUTTON] + }, +] + +SEQ_AUTHORIZE_START = [ + { + "display": "Load BB2 Landing Page ...", + "action": Action.LOAD_PAGE, + "params": [settings.HOSTNAME_URL] + }, + CLICK_TESTCLIENT, + { + "display": "Click link to get sample token v1/v2", + "action": Action.GET_SAMPLE_TOKEN_START, + }, + { + "display": "Click link 'Authorize as a Beneficiary' - start authorization", + "action": Action.FIND_CLICK, + "params": [30, By.LINK_TEXT, LNK_TXT_AUTH_AS_BENE] + }, +] + +SEQ_QUERY_FHIR_RESOURCES = [ + { + "display": "Click 'Patient' on FHIR resources page", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_PATIENT] + }, + { + "display": "Check Patient result page title", + "action": Action.CHECK, + "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, LNK_TXT_PATIENT] + }, + BROWSERBACK, + { + "display": "Click 'Coverage' on FHIR resources page", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_COVERAGE] + }, + { + "display": "Check Coverage result page title", + "action": Action.CHECK, + "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_BUNDLE_LABEL_FMT, LNK_TXT_COVERAGE] + }, + { + "display": "Check and click Coverage result page navigation links 'last'", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_NAV_LAST] + }, + CLICK_TESTCLIENT, + { + "display": "Click 'ExplanationOfBenefit' on FHIR resources page", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_EOB] + }, + { + "display": "Check ExplanationOfBenefit result page title", + "action": Action.CHECK, + "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_BUNDLE_LABEL_FMT, LNK_TXT_EOB] + }, + { + "display": "Check and click ExplanationOfBenefit result page navigation links 'last'", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_NAV_LAST] + }, + WAIT_SECONDS, + CLICK_TESTCLIENT, + WAIT_SECONDS, + { + "display": "Click 'Profile' on FHIR resources page", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_PROFILE] + }, + WAIT_SECONDS, + { + "display": "Check Profile result page title", + "action": Action.CHECK, + "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, + "{} (OIDC Userinfo)".format(LNK_TXT_PROFILE)] + }, + BROWSERBACK, + { + "display": "Click 'FHIR Metadata' on FHIR resources page", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_METADATA] + }, + { + "display": "Check FHIR Metadata result page title", + "action": Action.CHECK, + "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, LNK_TXT_METADATA] + }, + BROWSERBACK, + { + "display": "Click 'OIDC Discovery' on FHIR resources page", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_OIDC_DISCOVERY] + }, + { + "display": "Check OIDC Discovery result page title", + "action": Action.CHECK, + "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, LNK_TXT_OIDC_DISCOVERY] + }, + BROWSERBACK, +] + +SEQ_QUERY_FHIR_RESOURCES_NO_DEMO = [ + { + "display": "Click 'Patient' on FHIR resources page", + "action": Action.FIND_CLICK, + "params": [20, By.LINK_TEXT, LNK_TXT_PATIENT] + }, + { + "display": "Check Patient result page content (
) expect no permission message",
+        "action": Action.CONTAIN_TEXT,
+        "params": [20, By.TAG_NAME, CONTENT_FHIR_RESULTPAGE_PRE, MESSAGE_NO_PERMISSION]
+    },
+    BROWSERBACK,
+    {
+        "display": "Click 'Coverage' on FHIR resources page",
+        "action": Action.FIND_CLICK,
+        "params": [20, By.LINK_TEXT, LNK_TXT_COVERAGE]
+    },
+    {
+        "display": "Check Coverage result page title",
+        "action": Action.CHECK,
+        "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_BUNDLE_LABEL_FMT, LNK_TXT_COVERAGE]
+    },
+    {
+        "display": "Check and click Coverage result page navigation links 'last'",
+        "action": Action.FIND_CLICK,
+        "params": [20, By.LINK_TEXT, LNK_TXT_NAV_LAST]
+    },
+    CLICK_TESTCLIENT,
+    {
+        "display": "Click 'ExplanationOfBenefit' on FHIR resources page",
+        "action": Action.FIND_CLICK,
+        "params": [20, By.LINK_TEXT, LNK_TXT_EOB]
+    },
+    {
+        "display": "Check ExplanationOfBenefit result page title",
+        "action": Action.CHECK,
+        "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_BUNDLE_LABEL_FMT, LNK_TXT_EOB]
+    },
+    {
+        "display": "Check and click ExplanationOfBenefit result page navigation links 'last'",
+        "action": Action.FIND_CLICK,
+        "params": [20, By.LINK_TEXT, LNK_TXT_NAV_LAST]
+    },
+    WAIT_SECONDS,
+    CLICK_TESTCLIENT,
+    WAIT_SECONDS,
+    {
+        "display": "Click 'Profile' on FHIR resources page",
+        "action": Action.FIND_CLICK,
+        "params": [20, By.LINK_TEXT, LNK_TXT_PROFILE]
+    },
+    WAIT_SECONDS,
+    {
+        "display": "Check Profile result page content (
) expect no permission message",
+        "action": Action.CONTAIN_TEXT,
+        "params": [20, By.TAG_NAME, CONTENT_FHIR_RESULTPAGE_PRE, MESSAGE_NO_PERMISSION]
+    },
+    BROWSERBACK,
+    {
+        "display": "Click 'FHIR Metadata' on FHIR resources page",
+        "action": Action.FIND_CLICK,
+        "params": [20, By.LINK_TEXT, LNK_TXT_METADATA]
+    },
+    {
+        "display": "Check FHIR Metadata result page title",
+        "action": Action.CHECK,
+        "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, LNK_TXT_METADATA]
+    },
+    BROWSERBACK,
+    {
+        "display": "Click 'OIDC Discovery' on FHIR resources page",
+        "action": Action.FIND_CLICK,
+        "params": [20, By.LINK_TEXT, LNK_TXT_OIDC_DISCOVERY]
+    },
+    {
+        "display": "Check OIDC Discovery result page title",
+        "action": Action.CHECK,
+        "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, LNK_TXT_OIDC_DISCOVERY]
+    },
+    BROWSERBACK,
+]
+
+TESTS = {
+    "auth_grant_fhir_calls": [
+        {"sequence": SEQ_AUTHORIZE_START},
+        CALL_LOGIN,
+        CLICK_AGREE_ACCESS,
+        {"sequence": SEQ_QUERY_FHIR_RESOURCES}
+    ],
+    "auth_deny_fhir_calls": [
+        {"sequence": SEQ_AUTHORIZE_START},
+        CALL_LOGIN,
+        CLICK_DENY_ACCESS,
+        CHECK_TESTCLIENT_START_PAGE
+    ],
+    "auth_grant_w_no_demo": [
+        {"sequence": SEQ_AUTHORIZE_START},
+        CALL_LOGIN,
+        CLICK_RADIO_NOT_SHARE,
+        CLICK_AGREE_ACCESS,
+        {"sequence": SEQ_QUERY_FHIR_RESOURCES_NO_DEMO}
+    ]
+}
diff --git a/apps/integration_tests/selenium_tests.py b/apps/integration_tests/selenium_tests.py
index 15dee6b1b..aec162e7c 100755
--- a/apps/integration_tests/selenium_tests.py
+++ b/apps/integration_tests/selenium_tests.py
@@ -1,8 +1,6 @@
 import os
 import time
-from django.conf import settings
 from django.test import TestCase
-from enum import Enum
 
 from selenium import webdriver
 from selenium.webdriver.common.by import By
@@ -11,361 +9,18 @@
 from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
 from selenium.webdriver.common.keys import Keys
 
-TESTCLIENT_BUNDLE_LABEL_FMT = "Response (Bundle of {}), API version: {}"
-TESTCLIENT_RESOURCE_LABEL_FMT = "Response ({}), API version: {}"
-MESSAGE_NO_PERMISSION = "You do not have permission to perform this action."
-TESTCASE_BANNER_FMT = "** {} TEST: {}, API: {}, STEP: {}, {}"
-'''
-UI Widget text: texts on e.g. buttons, links, labels etc.
-'''
-LNK_TXT_TESTCLIENT = "Test Client"
-LNK_TXT_GET_TOKEN_V1 = "Get a Sample Authorization Token"
-LNK_TXT_GET_TOKEN_V2 = "Get a Sample Authorization Token for v2"
-LNK_TXT_AUTH_AS_BENE = "Authorize as a Beneficiary"
-LNK_TXT_RESTART_TESTCLIENT = "restart testclient"
-# FHIR search result bundle pagination
-LNK_TXT_NAV_FIRST = "first"
-LNK_TXT_NAV_NEXT = "next"
-LNK_TXT_NAV_PREV = "previous"
-LNK_TXT_NAV_LAST = "last"
-LNK_TXT_NAV_SELF = "self"
-# FHIR resources query page
-LNK_TXT_PATIENT = "Patient"
-LNK_TXT_EOB = "ExplanationOfBenefit"
-LNK_TXT_COVERAGE = "Coverage"
-LNK_TXT_PROFILE = "Profile"
-LNK_TXT_METADATA = "FHIR Metadata"
-LNK_TXT_OIDC_DISCOVERY = "OIDC Discovery"
-# FHIR result page label H2
-LAB_FHIR_RESULTPAGE_H2 = "h2"
-CONTENT_FHIR_RESULTPAGE_PRE = "pre"
-# MSLSX login form
-TXT_FLD_SUB_MSLSX = "username"
-TXT_FLD_HICN_MSLSX = "hicn"
-TXT_FLD_MBI_MSLSX = "mbi"
-TXT_FLD_VAL_SUB_MSLSX = "fred"
-MSLSX_TXT_FLD_HICN_VAL = "1000044680"
-MSLSX_TXT_FLD_MBI_VAL = "2SW4N00AA00"
-MSLSX_CSS_BUTTON = "button"
-# SLSX login form
-SLSX_TXT_FLD_USERNAME = "username-textbox"
-SLSX_TXT_FLD_PASSWORD = "password-textbox"
-SLSX_TXT_FLD_USERNAME_VAL = "BBUser00000"
-SLSX_TXT_FLD_PASSWORD_VAL = "PW00000!"
-SLSX_CSS_BUTTON = "login-button"
-# Demographic info access grant form
-BTN_ID_GRANT_DEMO_ACCESS = "approve"
-BTN_ID_DENY_DEMO_ACCESS = "deny"
-BTN_ID_RADIO_NOT_SHARE = "label:nth-child(5)"
-# API versions
-API_V2 = "v2"
-API_V1 = "v1"
-
-
-class Action(Enum):
-    LOAD_PAGE = 1
-    FIND_CLICK = 2
-    FIND = 3
-    FIND_SEND_KEY = 4
-    CHECK = 5
-    BACK = 6
-    LOGIN = 7
-    CONTAIN_TEXT = 8
-    GET_SAMPLE_TOKEN_START = 9
-    SLEEP = 10
-
-
-BROWSERBACK = {
-    "display": "Back to FHIR resource page",
-    "action": Action.BACK,
-}
-
-WAIT_SECONDS = {
-    "display": "Sleep seconds...",
-    "action": Action.SLEEP,
-    "params": [3],
-}
-
-CHECK_TESTCLIENT_START_PAGE = {
-    "display": "Check it's on 'Test Client' start page",
-    "action": Action.FIND,
-    "params": [30, By.LINK_TEXT, LNK_TXT_GET_TOKEN_V1]
-}
-
-CLICK_TESTCLIENT = {
-    "display": "Click link 'Test Client'",
-    "action": Action.FIND_CLICK,
-    "params": [30, By.LINK_TEXT, LNK_TXT_TESTCLIENT]
-}
-
-CLICK_RADIO_NOT_SHARE = {
-    "display": "Click 'Share healthcare data, but not your personal info' on DEMO info grant form",
-    "action": Action.FIND_CLICK,
-    "params": [20, By.CSS_SELECTOR, BTN_ID_RADIO_NOT_SHARE]
-}
-
-CLICK_AGREE_ACCESS = {
-    "display": "Click 'Agree' on DEMO info grant form",
-    "action": Action.FIND_CLICK,
-    "params": [20, By.ID, BTN_ID_GRANT_DEMO_ACCESS]
-}
-
-CLICK_DENY_ACCESS = {
-    "display": "Click 'Deny' on DEMO info grant form",
-    "action": Action.FIND_CLICK,
-    "params": [20, By.ID, BTN_ID_DENY_DEMO_ACCESS]
-}
-
-CALL_LOGIN = {
-    "display": "Start login ...",
-    "action": Action.LOGIN,
-}
-
-SEQ_LOGIN_MSLSX = [
-    {
-        "display": "Input SUB(username)",
-        "action": Action.FIND_SEND_KEY,
-        "params": [20, By.NAME, TXT_FLD_SUB_MSLSX, TXT_FLD_VAL_SUB_MSLSX]
-    },
-    {
-        "display": "Input hicn",
-        "action": Action.FIND_SEND_KEY,
-        "params": [20, By.NAME, TXT_FLD_HICN_MSLSX, MSLSX_TXT_FLD_HICN_VAL]
-    },
-    {
-        "display": "Input mbi",
-        "action": Action.FIND_SEND_KEY,
-        "params": [20, By.NAME, TXT_FLD_MBI_MSLSX, MSLSX_TXT_FLD_MBI_VAL]
-    },
-    {
-        "display": "Click 'submit' on MSLSX login form",
-        "action": Action.FIND_CLICK,
-        "params": [20, By.CSS_SELECTOR, MSLSX_CSS_BUTTON]
-    },
-]
-
-SEQ_LOGIN_SLSX = [
-    {
-        "display": "MyMedicare login username",
-        "action": Action.FIND_SEND_KEY,
-        "params": [20, By.ID, SLSX_TXT_FLD_USERNAME, SLSX_TXT_FLD_USERNAME_VAL]
-    },
-    {
-        "display": "MyMedicare login password",
-        "action": Action.FIND_SEND_KEY,
-        "params": [20, By.ID, SLSX_TXT_FLD_PASSWORD, SLSX_TXT_FLD_PASSWORD_VAL]
-    },
-    {
-        "display": "Click 'submit' on SLSX login form",
-        "action": Action.FIND_CLICK,
-        "params": [20, By.ID, SLSX_CSS_BUTTON]
-    },
-]
-
-SEQ_AUTHORIZE_START = [
-    {
-        "display": "Load BB2 Landing Page ...",
-        "action": Action.LOAD_PAGE,
-        "params": [settings.HOSTNAME_URL]
-    },
-    CLICK_TESTCLIENT,
-    {
-        "display": "Click link to get sample token v1/v2",
-        "action": Action.GET_SAMPLE_TOKEN_START,
-    },
-    {
-        "display": "Click link 'Authorize as a Beneficiary' - start authorization",
-        "action": Action.FIND_CLICK,
-        "params": [30, By.LINK_TEXT, LNK_TXT_AUTH_AS_BENE]
-    },
-]
-
-SEQ_QUERY_FHIR_RESOURCES = [
-    {
-        "display": "Click 'Patient' on FHIR resources page",
-        "action": Action.FIND_CLICK,
-        "params": [20, By.LINK_TEXT, LNK_TXT_PATIENT]
-    },
-    {
-        "display": "Check Patient result page title",
-        "action": Action.CHECK,
-        "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, LNK_TXT_PATIENT]
-    },
-    BROWSERBACK,
-    {
-        "display": "Click 'Coverage' on FHIR resources page",
-        "action": Action.FIND_CLICK,
-        "params": [20, By.LINK_TEXT, LNK_TXT_COVERAGE]
-    },
-    {
-        "display": "Check Coverage result page title",
-        "action": Action.CHECK,
-        "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_BUNDLE_LABEL_FMT, LNK_TXT_COVERAGE]
-    },
-    {
-        "display": "Check and click Coverage result page navigation links 'last'",
-        "action": Action.FIND_CLICK,
-        "params": [20, By.LINK_TEXT, LNK_TXT_NAV_LAST]
-    },
-    CLICK_TESTCLIENT,
-    {
-        "display": "Click 'ExplanationOfBenefit' on FHIR resources page",
-        "action": Action.FIND_CLICK,
-        "params": [20, By.LINK_TEXT, LNK_TXT_EOB]
-    },
-    {
-        "display": "Check ExplanationOfBenefit result page title",
-        "action": Action.CHECK,
-        "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_BUNDLE_LABEL_FMT, LNK_TXT_EOB]
-    },
-    {
-        "display": "Check and click ExplanationOfBenefit result page navigation links 'last'",
-        "action": Action.FIND_CLICK,
-        "params": [20, By.LINK_TEXT, LNK_TXT_NAV_LAST]
-    },
-    WAIT_SECONDS,
-    CLICK_TESTCLIENT,
-    WAIT_SECONDS,
-    {
-        "display": "Click 'Profile' on FHIR resources page",
-        "action": Action.FIND_CLICK,
-        "params": [20, By.LINK_TEXT, LNK_TXT_PROFILE]
-    },
-    WAIT_SECONDS,
-    {
-        "display": "Check Profile result page title",
-        "action": Action.CHECK,
-        "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT,
-                   "{} (OIDC Userinfo)".format(LNK_TXT_PROFILE)]
-    },
-    BROWSERBACK,
-    {
-        "display": "Click 'FHIR Metadata' on FHIR resources page",
-        "action": Action.FIND_CLICK,
-        "params": [20, By.LINK_TEXT, LNK_TXT_METADATA]
-    },
-    {
-        "display": "Check FHIR Metadata result page title",
-        "action": Action.CHECK,
-        "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, LNK_TXT_METADATA]
-    },
-    BROWSERBACK,
-    {
-        "display": "Click 'OIDC Discovery' on FHIR resources page",
-        "action": Action.FIND_CLICK,
-        "params": [20, By.LINK_TEXT, LNK_TXT_OIDC_DISCOVERY]
-    },
-    {
-        "display": "Check OIDC Discovery result page title",
-        "action": Action.CHECK,
-        "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, LNK_TXT_OIDC_DISCOVERY]
-    },
-    BROWSERBACK,
-]
-
-SEQ_QUERY_FHIR_RESOURCES_NO_DEMO = [
-    {
-        "display": "Click 'Patient' on FHIR resources page",
-        "action": Action.FIND_CLICK,
-        "params": [20, By.LINK_TEXT, LNK_TXT_PATIENT]
-    },
-    {
-        "display": "Check Patient result page content (
) expect no permission message",
-        "action": Action.CONTAIN_TEXT,
-        "params": [20, By.TAG_NAME, CONTENT_FHIR_RESULTPAGE_PRE, MESSAGE_NO_PERMISSION]
-    },
-    BROWSERBACK,
-    {
-        "display": "Click 'Coverage' on FHIR resources page",
-        "action": Action.FIND_CLICK,
-        "params": [20, By.LINK_TEXT, LNK_TXT_COVERAGE]
-    },
-    {
-        "display": "Check Coverage result page title",
-        "action": Action.CHECK,
-        "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_BUNDLE_LABEL_FMT, LNK_TXT_COVERAGE]
-    },
-    {
-        "display": "Check and click Coverage result page navigation links 'last'",
-        "action": Action.FIND_CLICK,
-        "params": [20, By.LINK_TEXT, LNK_TXT_NAV_LAST]
-    },
-    CLICK_TESTCLIENT,
-    {
-        "display": "Click 'ExplanationOfBenefit' on FHIR resources page",
-        "action": Action.FIND_CLICK,
-        "params": [20, By.LINK_TEXT, LNK_TXT_EOB]
-    },
-    {
-        "display": "Check ExplanationOfBenefit result page title",
-        "action": Action.CHECK,
-        "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_BUNDLE_LABEL_FMT, LNK_TXT_EOB]
-    },
-    {
-        "display": "Check and click ExplanationOfBenefit result page navigation links 'last'",
-        "action": Action.FIND_CLICK,
-        "params": [20, By.LINK_TEXT, LNK_TXT_NAV_LAST]
-    },
-    WAIT_SECONDS,
-    CLICK_TESTCLIENT,
-    WAIT_SECONDS,
-    {
-        "display": "Click 'Profile' on FHIR resources page",
-        "action": Action.FIND_CLICK,
-        "params": [20, By.LINK_TEXT, LNK_TXT_PROFILE]
-    },
-    WAIT_SECONDS,
-    {
-        "display": "Check Profile result page content (
) expect no permission message",
-        "action": Action.CONTAIN_TEXT,
-        "params": [20, By.TAG_NAME, CONTENT_FHIR_RESULTPAGE_PRE, MESSAGE_NO_PERMISSION]
-    },
-    BROWSERBACK,
-    {
-        "display": "Click 'FHIR Metadata' on FHIR resources page",
-        "action": Action.FIND_CLICK,
-        "params": [20, By.LINK_TEXT, LNK_TXT_METADATA]
-    },
-    {
-        "display": "Check FHIR Metadata result page title",
-        "action": Action.CHECK,
-        "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, LNK_TXT_METADATA]
-    },
-    BROWSERBACK,
-    {
-        "display": "Click 'OIDC Discovery' on FHIR resources page",
-        "action": Action.FIND_CLICK,
-        "params": [20, By.LINK_TEXT, LNK_TXT_OIDC_DISCOVERY]
-    },
-    {
-        "display": "Check OIDC Discovery result page title",
-        "action": Action.CHECK,
-        "params": [20, By.TAG_NAME, LAB_FHIR_RESULTPAGE_H2, TESTCLIENT_RESOURCE_LABEL_FMT, LNK_TXT_OIDC_DISCOVERY]
-    },
-    BROWSERBACK,
-]
-
-TESTS = {
-    "auth_grant_fhir_calls": [
-        {"sequence": SEQ_AUTHORIZE_START},
-        CALL_LOGIN,
-        CLICK_AGREE_ACCESS,
-        {"sequence": SEQ_QUERY_FHIR_RESOURCES}
-    ],
-    "auth_deny_fhir_calls": [
-        {"sequence": SEQ_AUTHORIZE_START},
-        CALL_LOGIN,
-        CLICK_DENY_ACCESS,
-        CHECK_TESTCLIENT_START_PAGE
-    ],
-    "auth_grant_w_no_demo": [
-        {"sequence": SEQ_AUTHORIZE_START},
-        CALL_LOGIN,
-        CLICK_RADIO_NOT_SHARE,
-        CLICK_AGREE_ACCESS,
-        {"sequence": SEQ_QUERY_FHIR_RESOURCES_NO_DEMO}
-    ]
-}
+from .selenium_cases import (
+    Action,
+    TESTCASE_BANNER_FMT,
+    LNK_TXT_GET_TOKEN_V1,
+    LNK_TXT_GET_TOKEN_V2,
+    LNK_TXT_RESTART_TESTCLIENT,
+    API_V2,
+    API_V1,
+    SEQ_LOGIN_MSLSX,
+    SEQ_LOGIN_SLSX,
+    TESTS,
+)
 
 
 class SeleniumTests(TestCase):
diff --git a/docker-compose/readme.md b/docker-compose/readme.md
index 5d31a0294..e9070eb5b 100644
--- a/docker-compose/readme.md
+++ b/docker-compose/readme.md
@@ -300,4 +300,15 @@ You can run selenium tests by following below steps:
      ./docker-compose/run_selenium_tests_local_keybase.sh slsx
      ```
 
-  3. To trouble shoot tests: point VNC client to localhost:6900
\ No newline at end of file
+  3. To trouble shoot tests (visualize webUI interaction): point VNC client to localhost:6900
+     1. requires installation of vnc viewer, password (secret)
+     2. also need to comment out webdriver headless option, as shown below:
+     ```
+        ./apps/integration_tests/selenium_tests.py: setUp():
+        ...
+        opt = webdriver.ChromeOptions()
+        opt.add_argument('--headless')
+        opt.add_argument("--disable-dev-shm-usage")
+        ...
+     ```
+
diff --git a/docker-compose/run_selenium_tests_local_keybase.sh b/docker-compose/run_selenium_tests_local_keybase.sh
index 3a5efbcbf..19f215f7f 100755
--- a/docker-compose/run_selenium_tests_local_keybase.sh
+++ b/docker-compose/run_selenium_tests_local_keybase.sh
@@ -77,7 +77,7 @@ else
     if [ $1 == "logit" ]
     then
       TEST_TYPE="--logit"
-      TESTS_LIST="apps.integration_tests.logging_tests.LoggingTests"
+      TESTS_LIST="apps.integration_tests.logging_tests.LoggingTests.test_auth_fhir_flows_logging"
       export DJANGO_SETTINGS_MODULE="hhs_oauth_server.settings.test_logging"
       export DJANGO_LOG_JSON_FORMAT_PRETTY=False
       mkdir -p docker-compose/tmp

From 55b1dfca3e00d83f8fd839c24cb1fa41e5527a0e Mon Sep 17 00:00:00 2001
From: James Fuqian 
Date: Thu, 15 Jul 2021 11:08:44 -0700
Subject: [PATCH 05/39] pick up changes from BB2-545

---
 apps/integration_tests/selenium_tests.py      |  2 +-
 docker-compose.selenium.yml                   | 67 +++----------------
 .../run_selenium_tests_local_keybase.sh       | 31 ++++++---
 3 files changed, 32 insertions(+), 68 deletions(-)

diff --git a/apps/integration_tests/selenium_tests.py b/apps/integration_tests/selenium_tests.py
index aec162e7c..95a9c7856 100755
--- a/apps/integration_tests/selenium_tests.py
+++ b/apps/integration_tests/selenium_tests.py
@@ -41,7 +41,7 @@ def setUp(self):
             print("wait_completed={}".format(SeleniumTests.wait_completed))
 
         opt = webdriver.ChromeOptions()
-        # opt.add_argument('--headless')
+        opt.add_argument('--headless')
         opt.add_argument("--disable-dev-shm-usage")
         opt.add_argument("--disable-web-security")
         opt.add_argument("--allow-running-insecure-content")
diff --git a/docker-compose.selenium.yml b/docker-compose.selenium.yml
index e66c670a2..4cf1a0bf6 100755
--- a/docker-compose.selenium.yml
+++ b/docker-compose.selenium.yml
@@ -2,20 +2,6 @@ version: '3'
 
 services:
   tests:
-    build:
-      context: ./
-      dockerfile: SeleniumDockerfile
-    command: python runtests.py apps.integration_tests.selenium_tests.SeleniumTests
-    environment:
-          - HOSTNAME_URL=${HOSTNAME_URL}
-          - USE_MSLSX=${USE_MSLSX}
-    volumes:
-      - .:/code
-    depends_on:
-      - bb2
-      - chrome
-
-  tests_w_slsx:
     build:
       context: ./
       dockerfile: SeleniumDockerfile
@@ -28,7 +14,7 @@ services:
     depends_on:
       - bb2slsx
       - chrome
-      
+
   chrome:
     image: selenium/node-chrome:4.0.0-beta-4-prerelease-20210517
     volumes:
@@ -83,7 +69,7 @@ services:
     command: msls
     ports:
       - "8080:8080"
-
+  
   db:
     image: postgres
     environment:
@@ -92,7 +78,7 @@ services:
     ports:
             - "5432:5432"
 
-  bb2:
+  bb2slsx:
     build: . 
     command: ./docker-compose/bluebutton_server_start.sh '${DB_MIGRATIONS}' '${SUPER_USER_NAME}' '${SUPER_USER_EMAIL}' '${SUPER_USER_PASSWORD}' '${BB20_ENABLE_REMOTE_DEBUG}' '${BB20_REMOTE_DEBUG_WAIT_ATTACH}'
     environment:
@@ -103,12 +89,12 @@ services:
             - DJANGO_SECURE_SESSION=False
             - FHIR_URL="https://prod-sbx.bfd.cms.gov"
             - DJANGO_FHIR_CERTSTORE=/code/docker-compose/certstore/
-            - DJANGO_MEDICARE_SLSX_REDIRECT_URI=http://bb2:8000/mymedicare/sls-callback
-            - DJANGO_MEDICARE_SLSX_LOGIN_URI=http://msls:8080/sso/authorize?client_id=bb2api
-            - DJANGO_SLSX_HEALTH_CHECK_ENDPOINT=http://msls:8080/health
-            - DJANGO_SLSX_TOKEN_ENDPOINT=http://msls:8080/sso/session
-            - DJANGO_SLSX_SIGNOUT_ENDPOINT=http://msls:8080/sso/signout
-            - DJANGO_SLSX_USERINFO_ENDPOINT=http://msls:8080/v1/users
+            - DJANGO_MEDICARE_SLSX_REDIRECT_URI=${DJANGO_MEDICARE_SLSX_REDIRECT_URI}
+            - DJANGO_MEDICARE_SLSX_LOGIN_URI=${DJANGO_MEDICARE_SLSX_LOGIN_URI}
+            - DJANGO_SLSX_HEALTH_CHECK_ENDPOINT=${DJANGO_SLSX_HEALTH_CHECK_ENDPOINT}
+            - DJANGO_SLSX_TOKEN_ENDPOINT=${DJANGO_SLSX_TOKEN_ENDPOINT}
+            - DJANGO_SLSX_SIGNOUT_ENDPOINT=${DJANGO_SLSX_SIGNOUT_ENDPOINT}
+            - DJANGO_SLSX_USERINFO_ENDPOINT=${DJANGO_SLSX_USERINFO_ENDPOINT}
             - DJANGO_SLSX_CLIENT_ID=bb2api
             - DJANGO_SLSX_CLIENT_SECRET=${DJANGO_SLSX_CLIENT_SECRET}
             - HOSTNAME_URL=${HOSTNAME_URL}
@@ -125,38 +111,3 @@ services:
     depends_on:
       - db
       - msls
-
-  bb2slsx:
-    build: . 
-    command: ./docker-compose/bluebutton_server_start.sh '${DB_MIGRATIONS}' '${SUPER_USER_NAME}' '${SUPER_USER_EMAIL}' '${SUPER_USER_PASSWORD}' '${BB20_ENABLE_REMOTE_DEBUG}' '${BB20_REMOTE_DEBUG_WAIT_ATTACH}'
-    environment:
-            - DJANGO_SETTINGS_MODULE=hhs_oauth_server.settings.dev
-            - DATABASES_CUSTOM=postgres://postgres:toor@db:5432/bluebutton
-            - OAUTHLIB_INSECURE_TRANSPORT=true
-            - DJANGO_DEFAULT_SAMPLE_FHIR_ID="-20140000008325"
-            - DJANGO_SECURE_SESSION=False
-            - FHIR_URL="https://prod-sbx.bfd.cms.gov"
-            - DJANGO_FHIR_CERTSTORE=/code/docker-compose/certstore/
-            - DJANGO_SLSX_CLIENT_ID=bb2api
-            - DJANGO_SLSX_CLIENT_SECRET=${DJANGO_SLSX_CLIENT_SECRET}
-            - HOSTNAME_URL=${HOSTNAME_URL}
-            - DJANGO_MEDICARE_SLSX_REDIRECT_URI=http://bb2slsx:8000/mymedicare/sls-callback
-            - DJANGO_MEDICARE_SLSX_LOGIN_URI=https://test.medicare.gov/sso/authorize?client_id=bb2api
-            - DJANGO_SLSX_HEALTH_CHECK_ENDPOINT=https://test.accounts.cms.gov/health
-            - DJANGO_SLSX_TOKEN_ENDPOINT=https://test.medicare.gov/sso/session
-            - DJANGO_SLSX_SIGNOUT_ENDPOINT=https://test.medicare.gov/sso/signout
-            - DJANGO_SLSX_USERINFO_ENDPOINT=https://test.accounts.cms.gov/v1/users
-            # SSL verify for internal endpoints can't currently use SSL verification (this may change in the future)
-            - DJANGO_SLSX_VERIFY_SSL_INTERNAL=False
-            - DJANGO_SLSX_VERIFY_SSL_EXTERNAL=True
-            - DJANGO_LOG_JSON_FORMAT_PRETTY=True
-            - DJANGO_USER_ID_ITERATIONS=${DJANGO_USER_ID_ITERATIONS}
-            - DJANGO_USER_ID_SALT=${DJANGO_USER_ID_SALT}
-    volumes:
-      - .:/code
-    ports:
-      - "8000:8000"
-      - "5678:5678"
-    depends_on:
-      - db
-          
\ No newline at end of file
diff --git a/docker-compose/run_selenium_tests_local_keybase.sh b/docker-compose/run_selenium_tests_local_keybase.sh
index 19f215f7f..876715b51 100755
--- a/docker-compose/run_selenium_tests_local_keybase.sh
+++ b/docker-compose/run_selenium_tests_local_keybase.sh
@@ -19,7 +19,7 @@ CERT_FILENAME="client_data_server_bluebutton_local_integration_tests_certificate
 KEY_FILENAME="client_data_server_bluebutton_local_integration_tests_private_key.pem"
 
 # BB2 service end point default
-HOSTNAME_URL="http://bb2:8000"
+HOSTNAME_URL="http://bb2slsx:8000"
 
 # Backend FHIR server to use for selenium tests with FHIR requests:
 FHIR_URL="https://prod-sbx.bfd.cms.gov"
@@ -42,11 +42,16 @@ echo_msg
 set -e -u -o pipefail
 
 USE_MSLSX=true
-DOCKER_COMPOSE_SERVICE="tests"
-
 TEST_TYPE="--selenium"
 export DJANGO_SETTINGS_MODULE="hhs_oauth_server.settings.dev"
 
+DJANGO_MEDICARE_SLSX_REDIRECT_URI="http://bb2slsx:8000/mymedicare/sls-callback"
+DJANGO_MEDICARE_SLSX_LOGIN_URI="http://msls:8080/sso/authorize?client_id=bb2api"
+DJANGO_SLSX_HEALTH_CHECK_ENDPOINT="http://msls:8080/health"
+DJANGO_SLSX_TOKEN_ENDPOINT="http://msls:8080/sso/session"
+DJANGO_SLSX_SIGNOUT_ENDPOINT="http://msls:8080/sso/signout"
+DJANGO_SLSX_USERINFO_ENDPOINT="http://msls:8080/v1/users"
+
 # Parse command line option
 if [ $# -eq 0 ]
 then
@@ -71,8 +76,12 @@ else
     if [ $1 == "slsx" ]
     then
       USE_MSLSX=false
-      DOCKER_COMPOSE_SERVICE="tests_w_slsx"
-      HOSTNAME_URL="http://bb2slsx:8000"
+      DJANGO_MEDICARE_SLSX_REDIRECT_URI="http://bb2slsx:8000/mymedicare/sls-callback"
+      DJANGO_MEDICARE_SLSX_LOGIN_URI="https://test.medicare.gov/sso/authorize?client_id=bb2api"
+      DJANGO_SLSX_HEALTH_CHECK_ENDPOINT="https://test.accounts.cms.gov/health"
+      DJANGO_SLSX_TOKEN_ENDPOINT="https://test.medicare.gov/sso/session"
+      DJANGO_SLSX_SIGNOUT_ENDPOINT="https://test.medicare.gov/sso/signout"
+      DJANGO_SLSX_USERINFO_ENDPOINT="https://test.accounts.cms.gov/v1/users"
     fi
     if [ $1 == "logit" ]
     then
@@ -90,7 +99,6 @@ else
 fi
 
 echo "DJANGO_SETTINGS_MODULE: " ${DJANGO_SETTINGS_MODULE}
-echo "DOCKER_COMPOSE_SERVICE: " ${DOCKER_COMPOSE_SERVICE}
 echo "HOSTNAME_URL: " ${HOSTNAME_URL}
 echo "TEST_TYPE: " ${TEST_TYPE}
 echo "TESTS: " ${TESTS_LIST}
@@ -188,7 +196,7 @@ cp ${keybase_cert_file} "${CERTSTORE_TEMPORARY_MOUNT_PATH}/ca.cert.pem"
 cp ${keybase_key_file} "${CERTSTORE_TEMPORARY_MOUNT_PATH}/ca.key.nocrypt.pem"
 
 # stop all before run selenium tests
-docker-compose -f docker-compose.selenium.yml stop
+docker-compose -f docker-compose.selenium.yml down
 
 export DJANGO_PASSWORD_HASH_ITERATIONS=${DJANGO_PASSWORD_HASH_ITERATIONS}
 export DJANGO_USER_ID_SALT=${DJANGO_USER_ID_SALT}
@@ -197,11 +205,16 @@ export DJANGO_SLSX_CLIENT_ID=${DJANGO_SLSX_CLIENT_ID}
 export DJANGO_SLSX_CLIENT_SECRET=${DJANGO_SLSX_CLIENT_SECRET}
 export HOSTNAME_URL=${HOSTNAME_URL}
 export USE_MSLSX=${USE_MSLSX}
+export DJANGO_MEDICARE_SLSX_REDIRECT_URI=${DJANGO_MEDICARE_SLSX_REDIRECT_URI}
+export DJANGO_MEDICARE_SLSX_LOGIN_URI=${DJANGO_MEDICARE_SLSX_LOGIN_URI}
+export DJANGO_SLSX_HEALTH_CHECK_ENDPOINT=${DJANGO_SLSX_HEALTH_CHECK_ENDPOINT}
+export DJANGO_SLSX_TOKEN_ENDPOINT=${DJANGO_SLSX_TOKEN_ENDPOINT}
+export DJANGO_SLSX_SIGNOUT_ENDPOINT=${DJANGO_SLSX_SIGNOUT_ENDPOINT}
+export DJANGO_SLSX_USERINFO_ENDPOINT=${DJANGO_SLSX_USERINFO_ENDPOINT}
 
 echo "Selenium tests ..."
 
-docker-compose -f docker-compose.selenium.yml run \
-${DOCKER_COMPOSE_SERVICE} bash -c "python runtests.py ${TEST_TYPE} ${TESTS_LIST}"
+docker-compose -f docker-compose.selenium.yml run tests bash -c "python runtests.py ${TEST_TYPE} ${TESTS_LIST}"
 
 # Remove certfiles from local directory
 echo_msg

From eab261fdf59d28a378a4b24e883d4de85777a60f Mon Sep 17 00:00:00 2001
From: James Fuqian 
Date: Tue, 20 Jul 2021 10:54:34 -0700
Subject: [PATCH 06/39] pick bb2 545 changes and rebase.

---
 SeleniumDockerfile => Dockerfile.selenium     | 25 +++++-----
 apps/integration_tests/selenium_tests.py      |  2 +-
 apps/mymedicare_cb/authorization.py           |  1 -
 docker-compose.selenium.yml                   |  2 +-
 .../run_selenium_tests_local_keybase.sh       | 49 +++++++++----------
 5 files changed, 37 insertions(+), 42 deletions(-)
 rename SeleniumDockerfile => Dockerfile.selenium (68%)

diff --git a/SeleniumDockerfile b/Dockerfile.selenium
similarity index 68%
rename from SeleniumDockerfile
rename to Dockerfile.selenium
index 759971700..c02274e76 100755
--- a/SeleniumDockerfile
+++ b/Dockerfile.selenium
@@ -1,15 +1,12 @@
-FROM selenium/standalone-chrome-debug
-
-ENV PYTHONUNBUFFERED 1
-USER root
-RUN apt-get update && apt-get install -yq python3.7 python3-pip
-RUN pip3 install --upgrade pip
-RUN pip3 install selenium
-RUN pip3 install psycopg2-binary==2.8.6
-RUN pip3 install pyyaml==5.4.1
-RUN pip3 install Pillow==8.2.0
-RUN mkdir /code
-ADD . /code/
-WORKDIR /code
-RUN make reqs-install-dev
+FROM selenium/standalone-chrome-debug
+
+ENV PYTHONUNBUFFERED 1
+USER root
+RUN apt-get update && apt-get install -yq python3.7 python3-pip
+RUN pip3 install --upgrade pip
+RUN pip3 install selenium psycopg2-binary==2.8.6 pyyaml==5.4.1 Pillow==8.2.0
+RUN mkdir /code
+ADD . /code/
+WORKDIR /code
+RUN make reqs-install-dev
 RUN ln -s /usr/bin/python3 /usr/local/bin/python
\ No newline at end of file
diff --git a/apps/integration_tests/selenium_tests.py b/apps/integration_tests/selenium_tests.py
index 95a9c7856..aec162e7c 100755
--- a/apps/integration_tests/selenium_tests.py
+++ b/apps/integration_tests/selenium_tests.py
@@ -41,7 +41,7 @@ def setUp(self):
             print("wait_completed={}".format(SeleniumTests.wait_completed))
 
         opt = webdriver.ChromeOptions()
-        opt.add_argument('--headless')
+        # opt.add_argument('--headless')
         opt.add_argument("--disable-dev-shm-usage")
         opt.add_argument("--disable-web-security")
         opt.add_argument("--allow-running-insecure-content")
diff --git a/apps/mymedicare_cb/authorization.py b/apps/mymedicare_cb/authorization.py
index b391a076b..88c40094a 100644
--- a/apps/mymedicare_cb/authorization.py
+++ b/apps/mymedicare_cb/authorization.py
@@ -197,7 +197,6 @@ def service_health_check(self, request):
         the BB2 /health/external check.
         """
         headers = self.slsx_common_headers(request)
-
         response = requests.get(self.healthcheck_endpoint,
                                 headers=headers,
                                 allow_redirects=False,
diff --git a/docker-compose.selenium.yml b/docker-compose.selenium.yml
index 4cf1a0bf6..e6511767f 100755
--- a/docker-compose.selenium.yml
+++ b/docker-compose.selenium.yml
@@ -4,7 +4,7 @@ services:
   tests:
     build:
       context: ./
-      dockerfile: SeleniumDockerfile
+      dockerfile: Dockerfile.selenium
     command: python runtests.py apps.integration_tests.selenium_tests.SeleniumTests
     environment:
           - HOSTNAME_URL=${HOSTNAME_URL}
diff --git a/docker-compose/run_selenium_tests_local_keybase.sh b/docker-compose/run_selenium_tests_local_keybase.sh
index 876715b51..e94de2fc9 100755
--- a/docker-compose/run_selenium_tests_local_keybase.sh
+++ b/docker-compose/run_selenium_tests_local_keybase.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # Run the selenium tests in docker
-# 
+#
 # NOTE:
 #
 #   1. You must be logged in to Keybase and have the BB2 team file system mounted.
@@ -19,7 +19,7 @@ CERT_FILENAME="client_data_server_bluebutton_local_integration_tests_certificate
 KEY_FILENAME="client_data_server_bluebutton_local_integration_tests_private_key.pem"
 
 # BB2 service end point default
-HOSTNAME_URL="http://bb2slsx:8000"
+export HOSTNAME_URL="http://bb2slsx:8000"
 
 # Backend FHIR server to use for selenium tests with FHIR requests:
 FHIR_URL="https://prod-sbx.bfd.cms.gov"
@@ -41,16 +41,17 @@ echo_msg
 # Set bash builtins for safety
 set -e -u -o pipefail
 
-USE_MSLSX=true
 TEST_TYPE="--selenium"
+
+export USE_MSLSX=true
 export DJANGO_SETTINGS_MODULE="hhs_oauth_server.settings.dev"
 
-DJANGO_MEDICARE_SLSX_REDIRECT_URI="http://bb2slsx:8000/mymedicare/sls-callback"
-DJANGO_MEDICARE_SLSX_LOGIN_URI="http://msls:8080/sso/authorize?client_id=bb2api"
-DJANGO_SLSX_HEALTH_CHECK_ENDPOINT="http://msls:8080/health"
-DJANGO_SLSX_TOKEN_ENDPOINT="http://msls:8080/sso/session"
-DJANGO_SLSX_SIGNOUT_ENDPOINT="http://msls:8080/sso/signout"
-DJANGO_SLSX_USERINFO_ENDPOINT="http://msls:8080/v1/users"
+export DJANGO_MEDICARE_SLSX_REDIRECT_URI="http://bb2slsx:8000/mymedicare/sls-callback"
+export DJANGO_MEDICARE_SLSX_LOGIN_URI="http://msls:8080/sso/authorize?client_id=bb2api"
+export DJANGO_SLSX_HEALTH_CHECK_ENDPOINT="http://msls:8080/health"
+export DJANGO_SLSX_TOKEN_ENDPOINT="http://msls:8080/sso/session"
+export DJANGO_SLSX_SIGNOUT_ENDPOINT="http://msls:8080/sso/signout"
+export DJANGO_SLSX_USERINFO_ENDPOINT="http://msls:8080/v1/users"
 
 # Parse command line option
 if [ $# -eq 0 ]
@@ -75,13 +76,13 @@ else
   else
     if [ $1 == "slsx" ]
     then
-      USE_MSLSX=false
-      DJANGO_MEDICARE_SLSX_REDIRECT_URI="http://bb2slsx:8000/mymedicare/sls-callback"
-      DJANGO_MEDICARE_SLSX_LOGIN_URI="https://test.medicare.gov/sso/authorize?client_id=bb2api"
-      DJANGO_SLSX_HEALTH_CHECK_ENDPOINT="https://test.accounts.cms.gov/health"
-      DJANGO_SLSX_TOKEN_ENDPOINT="https://test.medicare.gov/sso/session"
-      DJANGO_SLSX_SIGNOUT_ENDPOINT="https://test.medicare.gov/sso/signout"
-      DJANGO_SLSX_USERINFO_ENDPOINT="https://test.accounts.cms.gov/v1/users"
+      export USE_MSLSX=false
+      export DJANGO_MEDICARE_SLSX_REDIRECT_URI="http://bb2slsx:8000/mymedicare/sls-callback"
+      export DJANGO_MEDICARE_SLSX_LOGIN_URI="https://test.medicare.gov/sso/authorize?client_id=bb2api"
+      export DJANGO_SLSX_HEALTH_CHECK_ENDPOINT="https://test.accounts.cms.gov/health"
+      export DJANGO_SLSX_TOKEN_ENDPOINT="https://test.medicare.gov/sso/session"
+      export DJANGO_SLSX_SIGNOUT_ENDPOINT="https://test.medicare.gov/sso/signout"
+      export DJANGO_SLSX_USERINFO_ENDPOINT="https://test.accounts.cms.gov/v1/users"
     fi
     if [ $1 == "logit" ]
     then
@@ -198,24 +199,22 @@ cp ${keybase_key_file} "${CERTSTORE_TEMPORARY_MOUNT_PATH}/ca.key.nocrypt.pem"
 # stop all before run selenium tests
 docker-compose -f docker-compose.selenium.yml down
 
-export DJANGO_PASSWORD_HASH_ITERATIONS=${DJANGO_PASSWORD_HASH_ITERATIONS}
 export DJANGO_USER_ID_SALT=${DJANGO_USER_ID_SALT}
 export DJANGO_USER_ID_ITERATIONS=${DJANGO_USER_ID_ITERATIONS}
 export DJANGO_SLSX_CLIENT_ID=${DJANGO_SLSX_CLIENT_ID}
 export DJANGO_SLSX_CLIENT_SECRET=${DJANGO_SLSX_CLIENT_SECRET}
-export HOSTNAME_URL=${HOSTNAME_URL}
-export USE_MSLSX=${USE_MSLSX}
-export DJANGO_MEDICARE_SLSX_REDIRECT_URI=${DJANGO_MEDICARE_SLSX_REDIRECT_URI}
-export DJANGO_MEDICARE_SLSX_LOGIN_URI=${DJANGO_MEDICARE_SLSX_LOGIN_URI}
-export DJANGO_SLSX_HEALTH_CHECK_ENDPOINT=${DJANGO_SLSX_HEALTH_CHECK_ENDPOINT}
-export DJANGO_SLSX_TOKEN_ENDPOINT=${DJANGO_SLSX_TOKEN_ENDPOINT}
-export DJANGO_SLSX_SIGNOUT_ENDPOINT=${DJANGO_SLSX_SIGNOUT_ENDPOINT}
-export DJANGO_SLSX_USERINFO_ENDPOINT=${DJANGO_SLSX_USERINFO_ENDPOINT}
 
 echo "Selenium tests ..."
 
 docker-compose -f docker-compose.selenium.yml run tests bash -c "python runtests.py ${TEST_TYPE} ${TESTS_LIST}"
 
+# Stop containers after use
+echo_msg
+echo_msg "Stopping containers..."
+echo_msg
+
+docker-compose -f docker-compose.selenium.yml stop
+
 # Remove certfiles from local directory
 echo_msg
 echo_msg Shred and Remove certfiles from CERTSTORE_TEMPORARY_MOUNT_PATH=${CERTSTORE_TEMPORARY_MOUNT_PATH}

From bc675cd29fe406586b20bc6ede2b48dbee808d6a Mon Sep 17 00:00:00 2001
From: James Fuqian 
Date: Fri, 23 Jul 2021 12:40:07 -0700
Subject: [PATCH 07/39] merge

---
 docker-compose.selenium.yml                        | 2 +-
 docker-compose/run_selenium_tests_local_keybase.sh | 4 ++--
 runtests.py                                        | 3 ---
 3 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/docker-compose.selenium.yml b/docker-compose.selenium.yml
index e6511767f..dcc28caa1 100755
--- a/docker-compose.selenium.yml
+++ b/docker-compose.selenium.yml
@@ -88,7 +88,7 @@ services:
             - DJANGO_DEFAULT_SAMPLE_FHIR_ID="-20140000008325"
             - DJANGO_SECURE_SESSION=False
             - FHIR_URL="https://prod-sbx.bfd.cms.gov"
-            - DJANGO_FHIR_CERTSTORE=/code/docker-compose/certstore/
+            - DJANGO_FHIR_CERTSTORE=${DJANGO_FHIR_CERTSTORE}
             - DJANGO_MEDICARE_SLSX_REDIRECT_URI=${DJANGO_MEDICARE_SLSX_REDIRECT_URI}
             - DJANGO_MEDICARE_SLSX_LOGIN_URI=${DJANGO_MEDICARE_SLSX_LOGIN_URI}
             - DJANGO_SLSX_HEALTH_CHECK_ENDPOINT=${DJANGO_SLSX_HEALTH_CHECK_ENDPOINT}
diff --git a/docker-compose/run_selenium_tests_local_keybase.sh b/docker-compose/run_selenium_tests_local_keybase.sh
index dbef6e1da..aaa36425c 100755
--- a/docker-compose/run_selenium_tests_local_keybase.sh
+++ b/docker-compose/run_selenium_tests_local_keybase.sh
@@ -13,8 +13,7 @@
 KEYBASE_ENV_FILE="team/bb20/infrastructure/creds/ENV_secrets_for_local_integration_tests.env"
 KEYBASE_CERTFILES_SUBPATH="team/bb20/infrastructure/certs/local_integration_tests/fhir_client/certstore/"
 
-DJANGO_FHIR_CERTSTORE="/certstore"
-CERTSTORE_TEMPORARY_MOUNT_PATH="/tmp/certstore"
+CERTSTORE_TEMPORARY_MOUNT_PATH="./docker-compose/certstore"
 CERT_FILENAME="client_data_server_bluebutton_local_integration_tests_certificate.pem"
 KEY_FILENAME="client_data_server_bluebutton_local_integration_tests_private_key.pem"
 
@@ -45,6 +44,7 @@ TEST_TYPE="--selenium"
 
 export USE_MSLSX=true
 export DJANGO_SETTINGS_MODULE="hhs_oauth_server.settings.dev"
+export DJANGO_FHIR_CERTSTORE="/code/docker-compose/certstore"
 
 export DJANGO_MEDICARE_SLSX_REDIRECT_URI="http://bb2slsx:8000/mymedicare/sls-callback"
 export DJANGO_MEDICARE_SLSX_LOGIN_URI="http://msls:8080/sso/authorize?client_id=bb2api"
diff --git a/runtests.py b/runtests.py
index 50cd6b5ab..336589bfa 100644
--- a/runtests.py
+++ b/runtests.py
@@ -18,12 +18,9 @@
         --selenium  This optional flag indicates tests are to run in selenium test mode.
         Space separated list of Django tests to run.
 
-<<<<<<< HEAD
         --logit  This optional flag indicates tests are to run in logging integration test mode.
         Space separated list of Django tests to run.
 
-=======
->>>>>>> origin/master
     For example:
 
         $ docker-compose exec web python runtests.py apps.dot_ext.tests

From b15130a758ebd124d8daf9fc0371d94a0ca628aa Mon Sep 17 00:00:00 2001
From: James Fuqian 
Date: Fri, 27 Aug 2021 09:30:09 -0700
Subject: [PATCH 08/39] refactor test_logging settings, temp pull in cbc job
 selenium pipeline to facilitate reviewing and verifying.

---
 ...insfile.cbc-run-multi-pr-checks-w-selenium | 130 ++++++++++++++++++
 .../cbc-pod-deployment-config-w-selenium.yaml |  11 ++
 hhs_oauth_server/settings/test_logging.py     |  84 +++--------
 3 files changed, 158 insertions(+), 67 deletions(-)
 create mode 100755 Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium
 create mode 100755 Jenkinsfiles/cbc-pod-deployment-config-w-selenium.yaml

diff --git a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium
new file mode 100755
index 000000000..2348dd012
--- /dev/null
+++ b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium
@@ -0,0 +1,130 @@
+pipeline {
+  agent {
+    kubernetes {
+      defaultContainer "bb2-cbc-build-selenium"
+      yamlFile "Jenkinsfiles/cbc-pod-deployment-config-w-selenium.yaml"
+    }
+  }
+
+  environment {
+    USE_MSLSX = true
+    USE_DEBUG = false
+    DJANGO_SLSX_VERIFY_SSL_INTERNAL = false
+    DJANGO_SLSX_VERIFY_SSL_EXTERNAL = false
+    DJANGO_LOG_JSON_FORMAT_PRETTY = true
+    DJANGO_SETTINGS_MODULE = "hhs_oauth_server.settings.dev"
+    OAUTHLIB_INSECURE_TRANSPORT = true
+    DJANGO_SECURE_SESSION = false
+    DJANGO_FHIR_CERTSTORE = "./certstore"
+    DJANGO_MEDICARE_SLSX_REDIRECT_URI = "http://localhost:8000/mymedicare/sls-callback"
+    DJANGO_MEDICARE_SLSX_LOGIN_URI = "http://localhost:8080/sso/authorize?client_id=bb2api"
+    DJANGO_SLSX_HEALTH_CHECK_ENDPOINT = "http://localhost:8080/health"
+    DJANGO_SLSX_TOKEN_ENDPOINT = "http://localhost:8080/sso/session"
+    DJANGO_SLSX_SIGNOUT_ENDPOINT = "http://localhost:8080/sso/signout"
+    DJANGO_SLSX_USERINFO_ENDPOINT = "http://localhost:8080/v1/users"
+    DJANGO_SLSX_CLIENT_ID = credentials("bb2-selenium-tests-slsx-client-id")
+    DJANGO_SLSX_CLIENT_SECRET = credentials("bb2-selenium-tests-slsx-client-secret")
+    DJANGO_USER_ID_ITERATIONS = credentials("bb2-integration-tests-bfd-iterations")
+    DJANGO_USER_ID_SALT = credentials("bb2-integration-tests-bfd-salt")
+    FHIR_CERT = credentials("bb2-integration-tests-bfd-cert")
+    FHIR_KEY = credentials("bb2-integration-tests-bfd-key")
+    FHIR_URL = "${params.FHIR_URL}"
+  }
+
+  parameters {
+    string(
+      name: 'FHIR_URL',
+      defaultValue: "https://prod-sbx.bfd.cms.gov",
+      description: 'The default FHIR URL for the back end BFD service.'
+    )
+    booleanParam(
+        name: 'RUN_SELENIUM_TESTS', 
+        defaultValue: true, 
+        description: 'Set to true, selenium tests will be run as part of integration tests'
+    )
+  }
+
+  stages {
+    stage("SETUP FHIR cert and key") {
+      steps {
+        writeFile(file: "${env.DJANGO_FHIR_CERTSTORE}/certstore/ca.cert.pem", text: readFile(env.FHIR_CERT))
+        writeFile(file: "${env.DJANGO_FHIR_CERTSTORE}/certstore/ca.key.nocrypt.pem", text: readFile(env.FHIR_KEY))
+      }
+    }
+
+    stage("INSTALL Python Packages") {
+      steps {
+        sh """
+          python -m venv venv
+          . venv/bin/activate
+          make reqs-install-dev
+          pip install selenium
+        """
+      }
+    }
+
+    stage("CHECK Flake8 Python Lint/Style") {
+      steps{
+        sh """
+          . venv/bin/activate
+          flake8
+        """
+      }
+    }
+
+    stage("RUN Django Unit Tests") {
+      steps{
+        sh """
+          . venv/bin/activate
+          python runtests.py
+        """
+      }
+    }
+
+    stage("START MSLSX in background") {
+       when {
+        expression { params.RUN_SELENIUM_TESTS == true }
+       }
+       steps{
+        sh """
+          . venv/bin/activate
+          cd dev-local && export DJANGO_SETTINGS_MODULE=mslsx_django.settings && python manage.py migrate && echo 'starting mslsx ...' && (python manage.py runserver 0.0.0.0:8080 &)
+        """
+      }
+    }
+
+    stage("START BB2 server in background") {
+       when {
+        expression { params.RUN_SELENIUM_TESTS == true }
+       }
+       steps{
+        sh """
+          . venv/bin/activate
+          export DJANGO_SETTINGS_MODULE=hhs_oauth_server.settings.dev && python manage.py migrate && python manage.py create_admin_groups && python manage.py loaddata scopes.json && python manage.py create_blue_button_scopes && python manage.py create_test_user_and_application && python manage.py create_user_identification_label_selection && python manage.py create_test_feature_switches && (if [ ! -d 'bluebutton-css' ] ; then git clone https://github.com/CMSgov/bluebutton-css.git ; else echo 'CSS already installed.' ; fi) && echo 'starting bb2...' && (python manage.py runserver 0.0.0.0:8000 &)
+        """
+       }
+    }
+
+    stage("RUN integration tests") {
+      steps{
+        sh """
+          . venv/bin/activate
+          python runtests.py --integration apps.integration_tests.integration_test_fhir_resources.IntegrationTestFhirApiResources
+        """
+      }
+    }
+
+    stage("RUN selenium tests") {
+       when {
+        expression { params.RUN_SELENIUM_TESTS == true }
+       }
+       steps{
+        sh """
+          . venv/bin/activate
+          python runtests.py --selenium apps.integration_tests.selenium_tests.SeleniumTests
+        """
+       }
+    }
+
+  }
+}
diff --git a/Jenkinsfiles/cbc-pod-deployment-config-w-selenium.yaml b/Jenkinsfiles/cbc-pod-deployment-config-w-selenium.yaml
new file mode 100755
index 000000000..b62af702e
--- /dev/null
+++ b/Jenkinsfiles/cbc-pod-deployment-config-w-selenium.yaml
@@ -0,0 +1,11 @@
+apiVersion: v1
+kind: Pod
+spec:
+  containers:
+    - name: bb2-cbc-build-selenium
+      image: "public.ecr.aws/f5g8o1y9/bb2-cbc-build-selenium:latest"
+      tty: true
+      command: ["tail", "-f"]
+      imagePullPolicy: Always
+  nodeSelector:
+      Agents: true
diff --git a/hhs_oauth_server/settings/test_logging.py b/hhs_oauth_server/settings/test_logging.py
index da344474a..d90642520 100755
--- a/hhs_oauth_server/settings/test_logging.py
+++ b/hhs_oauth_server/settings/test_logging.py
@@ -1,70 +1,20 @@
-from .test import *
+from .dev import *
 
 # Use env-specific logging config if present
-LOGGING = env("DJANGO_LOGGING", {
-    'version': 1,
-    'disable_existing_loggers': False,
-    'formatters': {
-        'verbose': {
-            'format': '%(asctime)s %(levelname)s '
-                      '[%(process)d] %(name)s line:%(lineno)d %(message)s'
-        },
-        'simple': {
-            'format': '%(asctime)s %(levelname)s %(name)s %(message)s'
-        },
-        'jsonout': {
-            'format': '{"env": "' + env('TARGET_ENV', 'DEV') + '", "time": "%(asctime)s", "level": "%(levelname)s", '
-                      '"name": "%(name)s", "message": "%(message)s"}',
-            'datefmt': '%Y-%m-%d %H:%M:%S'
+# Add file handler.
+LOGGING['handlers']['file'] = {
+    'class': 'logging.FileHandler',
+    'filename': '/code/docker-compose/tmp/bb2_logging_test.log',
+}
 
-        }
-    },
-    'handlers': {
-        'console': {
-            'class': 'logging.StreamHandler',
-            'formatter': 'verbose',
-        },
-        'file': {
-            'class': 'logging.FileHandler',
-            'filename': '/code/docker-compose/tmp/bb2_logging_test.log',
-        },
-    },
-    'loggers': {
-        'hhs_server': {
-            'handlers': ['console', 'file'],
-            'level': 'INFO',
-        },
-        'hhs_oauth_server.accounts': {
-            'handlers': ['console'],
-            'level': 'INFO',
-        },
-        'oauth2_provider': {
-            'handlers': ['console'],
-            'level': 'INFO',
-        },
-        'oauthlib': {
-            'handlers': ['console'],
-            'level': 'INFO',
-        },
-        'unsuccessful_logins': {
-            'handlers': ['console'],
-            'level': 'INFO',
-        },
-        'admin_interface': {
-            'handlers': ['console'],
-            'level': 'INFO',
-        },
-        'tests': {
-            'handlers': ['console'],
-            'level': 'DEBUG',
-        },
-        'audit': {
-            'handlers': ['console', 'file'],
-            'level': 'INFO',
-        },
-        'performance': {
-            'handlers': ['console'],
-            'level': 'INFO',
-        }
-    },
-})
+if LOGGING.get('loggers'):
+    # Update hhs_server logger
+    LOGGING.get['loggers']['hhs_server'] = {
+        'handlers': ['console', 'file'],
+        'level': 'INFO',
+    }
+    # Update audit logger
+    LOGGING['loggers']['audit'] = {
+        'handlers': ['console', 'file'],
+        'level': 'INFO',
+    }

From 8fe0aa522a9aeb9e14bca82367344cf070db5899 Mon Sep 17 00:00:00 2001
From: James Fuqian 
Date: Mon, 30 Aug 2021 15:12:10 -0700
Subject: [PATCH 09/39] refactor, hookup with selenium tests job.

---
 .dockerignore                                 |   7 +
 Dockerfile.selenium                           |   2 +-
 Dockerfiles/Dockerfile.jenkins                |  20 ++
 Dockerfiles/readme.md                         |  13 +
 apps/fhir/server/authentication.py            |   4 +-
 .../integration_test_fhir_resources.py        |  50 ++--
 apps/integration_tests/selenium_tests.py      |  20 +-
 dev-local/manage.py                           |  21 ++
 dev-local/msls/Dockerfile                     |   8 -
 dev-local/msls/go.mod                         |   3 -
 dev-local/msls/login_form.go                  |  71 -----
 dev-local/msls/main.go                        | 279 ------------------
 dev-local/mslsx_django/__init__.py            |   0
 dev-local/mslsx_django/settings.py            | 121 ++++++++
 dev-local/mslsx_django/templates/login.html   |  75 +++++
 dev-local/mslsx_django/urls.py                |  20 ++
 dev-local/mslsx_django/views.py               | 164 ++++++++++
 dev-local/mslsx_django/wsgi.py                |  16 +
 docker-compose.selenium.yml                   |  80 ++---
 docker-compose.yml                            |   9 +-
 docker-compose/readme.md                      |  25 +-
 .../run_selenium_tests_local_keybase.sh       |  36 ++-
 hhs_oauth_server/settings/test_logging.py     |   8 +-
 runtests.py                                   |   4 +-
 24 files changed, 584 insertions(+), 472 deletions(-)
 create mode 100755 .dockerignore
 create mode 100755 Dockerfiles/Dockerfile.jenkins
 create mode 100755 Dockerfiles/readme.md
 create mode 100755 dev-local/manage.py
 delete mode 100644 dev-local/msls/Dockerfile
 delete mode 100644 dev-local/msls/go.mod
 delete mode 100644 dev-local/msls/login_form.go
 delete mode 100644 dev-local/msls/main.go
 create mode 100755 dev-local/mslsx_django/__init__.py
 create mode 100755 dev-local/mslsx_django/settings.py
 create mode 100755 dev-local/mslsx_django/templates/login.html
 create mode 100755 dev-local/mslsx_django/urls.py
 create mode 100755 dev-local/mslsx_django/views.py
 create mode 100755 dev-local/mslsx_django/wsgi.py

diff --git a/.dockerignore b/.dockerignore
new file mode 100755
index 000000000..684acecaf
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,7 @@
+# exclude when build image
+apidocs/
+splunk/
+bluebutton-css/
+apidocs/
+bluebutton-openapi-doc/
+*.sh
\ No newline at end of file
diff --git a/Dockerfile.selenium b/Dockerfile.selenium
index 25c601384..ad68b60cb 100755
--- a/Dockerfile.selenium
+++ b/Dockerfile.selenium
@@ -2,7 +2,7 @@ FROM selenium/standalone-chrome-debug
 
 ENV PYTHONUNBUFFERED 1
 USER root
-RUN apt-get update && apt-get install -yq python3.7 python3-pip
+RUN apt-get update && apt-get install -yq python3.7 python3-pip git
 RUN pip3 install --upgrade pip
 RUN pip3 install selenium psycopg2-binary==2.8.6 pyyaml==5.4.1 Pillow==8.3.1
 RUN mkdir /code
diff --git a/Dockerfiles/Dockerfile.jenkins b/Dockerfiles/Dockerfile.jenkins
new file mode 100755
index 000000000..fd47b8f92
--- /dev/null
+++ b/Dockerfiles/Dockerfile.jenkins
@@ -0,0 +1,20 @@
+FROM python:3.7.10
+# For build CBC Jenkins job ECR image
+ENV PYTHONUNBUFFERED 1
+# ENV PYTHONDEVMODE 1
+
+RUN mkdir /code
+ADD . /code/
+WORKDIR /code
+
+RUN apt-get update && apt-get install -yq git unzip curl
+
+# Install Chrome for Selenium
+RUN curl https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb -o /chrome.deb \
+    && dpkg -i /chrome.deb || apt-get install -yf \
+    && rm /chrome.deb
+
+# Install chromedriver for Selenium
+RUN wget -O /tmp/chromedriver.zip http://chromedriver.storage.googleapis.com/`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE`/chromedriver_linux64.zip \
+    && unzip /tmp/chromedriver.zip chromedriver -d /usr/local/bin/ \
+    && chmod +x /usr/local/bin/chromedriver
diff --git a/Dockerfiles/readme.md b/Dockerfiles/readme.md
new file mode 100755
index 000000000..4725b9872
--- /dev/null
+++ b/Dockerfiles/readme.md
@@ -0,0 +1,13 @@
+# Build, Tag, and Publish integration and selenium tests ECR iamge
+
+Go to BB2 local repo base directory and do the followings (assume aws cli installed and configured properly):
+
+```
+
+aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/f5g8o1y9
+cd /Dockerfiles
+docker build -f Dockerfile.jenkins -t bb2-cbc-build-selenium .
+docker tag bb2-cbc-build-selenium:latest public.ecr.aws/f5g8o1y9/bb2-cbc-build-selenium:latest
+docker push public.ecr.aws/f5g8o1y9/bb2-cbc-build-selenium:latest
+
+```
\ No newline at end of file
diff --git a/apps/fhir/server/authentication.py b/apps/fhir/server/authentication.py
index 816f7b483..89af9938c 100644
--- a/apps/fhir/server/authentication.py
+++ b/apps/fhir/server/authentication.py
@@ -4,12 +4,12 @@
 from rest_framework import exceptions
 
 from apps.dot_ext.loggers import get_session_auth_flow_trace
-from apps.fhir.bluebutton.utils import (generate_info_headers,
-                                        set_default_header)
 from apps.fhir.bluebutton.signals import (
     pre_fetch,
     post_fetch
 )
+from apps.fhir.bluebutton.utils import (generate_info_headers,
+                                        set_default_header)
 
 from ..bluebutton.exceptions import UpstreamServerException
 from ..bluebutton.utils import (FhirServerAuth,
diff --git a/apps/integration_tests/integration_test_fhir_resources.py b/apps/integration_tests/integration_test_fhir_resources.py
index 40f84b6ef..5a4bd9e97 100644
--- a/apps/integration_tests/integration_test_fhir_resources.py
+++ b/apps/integration_tests/integration_test_fhir_resources.py
@@ -186,17 +186,20 @@ def test_health_external_endpoint_v2(self):
         self._call_health_external_endpoint(True)
 
     def _call_health_external_endpoint(self, v2=False):
-        client = APIClient()
-        # no authenticate needed
-        response = client.get(self.live_server_url + "/health/external_v2" if v2 else "/health/external")
-        self.assertEqual(response.status_code, 200)
-        content = json.loads(response.content)
-        msg = None
-        try:
-            msg = content['message']
-        except KeyError:
-            pass
-        self.assertEqual(msg, "all's well")
+        use_mslsx = os.environ.get('USE_MSLSX', None)
+        if use_mslsx is not None and not use_mslsx == 'true':
+            # do not ping health end point if using MSLSX
+            client = APIClient()
+            # no authenticate needed
+            response = client.get(self.live_server_url + "/health/external_v2" if v2 else "/health/external")
+            self.assertEqual(response.status_code, 200)
+            content = json.loads(response.content)
+            msg = None
+            try:
+                msg = content['message']
+            except KeyError:
+                pass
+            self.assertEqual(msg, "all's well")
 
     @override_switch('require-scopes', active=True)
     def test_health_bfd_endpoint(self):
@@ -236,17 +239,20 @@ def test_health_db_endpoint(self):
 
     @override_switch('require-scopes', active=True)
     def test_health_sls_endpoint(self):
-        client = APIClient()
-        # no authenticate needed
-        response = client.get(self.live_server_url + "/health/sls")
-        self.assertEqual(response.status_code, 200)
-        content = json.loads(response.content)
-        msg = None
-        try:
-            msg = content['message']
-        except KeyError:
-            pass
-        self.assertEqual(msg, "all's well")
+        use_mslsx = os.environ.get('USE_MSLSX', None)
+        if use_mslsx is not None and not use_mslsx == 'true':
+            # do not ping health end point if using MSLSX
+            client = APIClient()
+            # no authenticate needed
+            response = client.get(self.live_server_url + "/health/sls")
+            self.assertEqual(response.status_code, 200)
+            content = json.loads(response.content)
+            msg = None
+            try:
+                msg = content['message']
+            except KeyError:
+                pass
+            self.assertEqual(msg, "all's well")
 
     @override_switch('require-scopes', active=True)
     def test_userinfo_endpoint(self):
diff --git a/apps/integration_tests/selenium_tests.py b/apps/integration_tests/selenium_tests.py
index 95a9c7856..292675dfb 100755
--- a/apps/integration_tests/selenium_tests.py
+++ b/apps/integration_tests/selenium_tests.py
@@ -32,7 +32,7 @@ class SeleniumTests(TestCase):
 
     def setUp(self):
         super(SeleniumTests, self).setUp()
-        # a bit waiting for selenium service ready for sure
+        # a bit waiting for selenium services ready for sure
         if not SeleniumTests.wait_completed:
             time.sleep(20)
             SeleniumTests.wait_completed = True
@@ -40,8 +40,12 @@ def setUp(self):
         else:
             print("wait_completed={}".format(SeleniumTests.wait_completed))
 
+        self.use_mslsx = os.environ['USE_MSLSX']
+        self.use_debug = os.environ['USE_DEBUG']
+        self.login_seq = SEQ_LOGIN_MSLSX if self.use_mslsx == 'true' else SEQ_LOGIN_SLSX
+        print("use_mslsx={}, use_debug={}".format(self.use_mslsx, self.use_debug))
+
         opt = webdriver.ChromeOptions()
-        opt.add_argument('--headless')
         opt.add_argument("--disable-dev-shm-usage")
         opt.add_argument("--disable-web-security")
         opt.add_argument("--allow-running-insecure-content")
@@ -54,9 +58,13 @@ def setUp(self):
         opt.add_argument('--window-size=1920,1080')
         opt.add_argument("--whitelisted-ips=''")
 
-        self.driver = webdriver.Remote(
-            command_executor='http://selenium-hub:4444',
-            desired_capabilities=DesiredCapabilities.CHROME, options=opt)
+        if self.use_debug == 'true':
+            self.driver = webdriver.Remote(
+                command_executor='http://chrome:4444/wd/hub',
+                desired_capabilities=DesiredCapabilities.CHROME, options=opt)
+        else:
+            opt.add_argument('--headless')
+            self.driver = webdriver.Chrome(options=opt)
 
         self.actions = {
             Action.LOAD_PAGE: self._load_page,
@@ -70,8 +78,6 @@ def setUp(self):
             Action.LOGIN: self._login,
             Action.SLEEP: self._sleep,
         }
-        self.use_mslsx = os.environ['USE_MSLSX']
-        self.login_seq = SEQ_LOGIN_MSLSX if self.use_mslsx == 'true' else SEQ_LOGIN_SLSX
 
     def tearDown(self):
         self.driver.quit()
diff --git a/dev-local/manage.py b/dev-local/manage.py
new file mode 100755
index 000000000..bca5b1fb7
--- /dev/null
+++ b/dev-local/manage.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+"""Django's command-line utility for administrative tasks."""
+import os
+import sys
+
+
+def main():
+    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mslsx_django.settings')
+    try:
+        from django.core.management import execute_from_command_line
+    except ImportError as exc:
+        raise ImportError(
+            "Couldn't import Django. Are you sure it's installed and "
+            "available on your PYTHONPATH environment variable? Did you "
+            "forget to activate a virtual environment?"
+        ) from exc
+    execute_from_command_line(sys.argv)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/dev-local/msls/Dockerfile b/dev-local/msls/Dockerfile
deleted file mode 100644
index 38551e45e..000000000
--- a/dev-local/msls/Dockerfile
+++ /dev/null
@@ -1,8 +0,0 @@
-FROM golang:alpine
-
-WORKDIR /go/src/app
-COPY . .
-
-RUN go install -v .
-
-CMD ["app"]
diff --git a/dev-local/msls/go.mod b/dev-local/msls/go.mod
deleted file mode 100644
index 3bab48201..000000000
--- a/dev-local/msls/go.mod
+++ /dev/null
@@ -1,3 +0,0 @@
-module github.com/whytheplatypus/msls
-
-go 1.12
diff --git a/dev-local/msls/login_form.go b/dev-local/msls/login_form.go
deleted file mode 100644
index 66579ec77..000000000
--- a/dev-local/msls/login_form.go
+++ /dev/null
@@ -1,71 +0,0 @@
-package main
-
-var login_template = `
-
-
-

MSLSX Component Inputs for simulating SLSX/MyMedicare auth flow locally

-

-BB2 Local Dev Warning -

Simulated Authentication no PHI or PII

-

Use Only Synthetic Beneficiary Info

-

Enter values to be returned by the SLSX user_info endpoint below:

-

-
-
-
-
-

Enter User Name:

- -
-

Enter Identity:

- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- - - -
-
-` diff --git a/dev-local/msls/main.go b/dev-local/msls/main.go deleted file mode 100644 index e69568bf4..000000000 --- a/dev-local/msls/main.go +++ /dev/null @@ -1,279 +0,0 @@ -package main - -import ( - "encoding/base64" - "encoding/json" - "fmt" - "html/template" - "log" - "net/http" - "net/http/httputil" - "net/url" - "strings" -) - -/* - accept regular endpoints and return sain responses - match usernames and passwords, but as plain text - 1. login screen - 2. redirect with code - 3. accept code post - 4. return token - 5. return info on userinfo endpoint - - -MEDICARE_LOGIN_URI = env('DJANGO_MEDICARE_LOGIN_URI', - 'https://dev2.account.mymedicare.gov/?scope=openid%20profile&client_id=bluebutton') -MEDICARE_REDIRECT_URI = env( - 'DJANGO_MEDICARE_REDIRECT_URI', 'http://localhost:8000/mymedicare/sls-callback') -SLS_USERINFO_ENDPOINT = env( - 'DJANGO_SLS_USERINFO_ENDPOINT', 'https://dev.accounts.cms.gov/v1/oauth/userinfo') -SLS_TOKEN_ENDPOINT = env( - 'DJANGO_SLS_TOKEN_ENDPOINT', 'https://dev.accounts.cms.gov/v1/oauth/token') - -MEDICARE_SLSX_LOGIN_URI=env('DJANGO_MEDICARE_SLSX_LOGIN_URI', - 'https://test.medicare.gov/sso/authorize?client_id=bb2api') -MEDICARE_SLSX_REDIRECT_URI=env('DJANGO_MEDICARE_SLSX_REDIRECT_URI', - 'http://localhost:8000/mymedicare/sls-callback') -SLSX_USERINFO_ENDPOINT=env('DJANGO_SLSX_USERINFO_ENDPOINT', 'https://test.accounts.cms.gov/v1/users') -SLSX_TOKEN_ENDPOINT=env('DJANGO_SLSX_TOKEN_ENDPOINT', 'https://test.medicare.gov/sso/session') - -SLSX exchange for auth_token: - -{ -"auth_token":"6yemAjHlDCa15RxXXNr15hfd/Q6Uvcc11KbgXhp/AgrJ", -"role":"consumer", -"user_id":"0854b569-5d28-4aa7-9878-fccdb15b4ffc", -"session_id":"36106cbd6982479c8c1dce52d8fb1588"}' - - -Sample SLSX response: - -{ - "status": "ok", - "code": 200, - "data": { - "user": { - "id": "0854b569-5d28-4aa7-9878-fccdb15b4ffc", - "username": "BbUser10000", - "email": null, - "firstName": null, - "middleName": null, - "lastName": null, - "suffix": null, - "dateOfBirth": null, - "ssn": null, - "city": null, - "state": null, - "addressLine1": null, - "addressLine2": null, - "zipcode": null, - "zipcodeExtension": null, - "phoneNumber": null, - "challenges": null, - "webBrokerInfo": null, - "isManuallyDisabled": false, - "requiresConfirm": null, - "loa": 0, - "loaAdminReason": null, - "lastLoginTime": 1617920826, - "createdTime": 1594071993, - "updatedTime": 1617920826, - "hicn": "1000087197", - "passwordExpirationTime": 1680992826, - "isDisabled": false, - "customUserInfo": { - "mbi": "2S17E00AA00" - }, - "mbi": "2S17E00AA00" - } - } - } - - - */ - -var signed_in = true - -const ( - ID_FIELD = "id" - USERNAME_FIELD = "username" - NAME_FIELD = "name" - EMAIL_FIELD = "email" - FIRST_NAME_FIELD = "fisrt_name" - LAST_NAME_FIELD = "last_name" - HICN_FIELD = "hicn" - MBI_FIELD = "mbi" - CODE_KEY = "code" - AUTH_HEADER = "Authorization" -) - -func logRequest(w http.Handler) http.Handler { - return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - v, err := httputil.DumpRequest(r, true) - log.Printf("%q : %s", v, err) - w.ServeHTTP(rw, r) - }) -} - -func main() { - t := template.Must(template.New("loginpage").Parse(login_template)) - http.Handle("/", logRequest(presentLogin(t))) - - http.Handle("/health", logRequest(http.HandlerFunc(handleHealth))) - http.Handle("/login", logRequest(http.HandlerFunc(handleLogin))) - http.Handle("/sso/session", logRequest(http.HandlerFunc(handleCode))) - http.Handle("/sso/signout", logRequest(http.HandlerFunc(handleSignout))) - http.Handle("/v1/users/", logRequest(http.HandlerFunc(handleUserinfo))) - http.ListenAndServe(":8080", nil) -} - -func handleCode(rw http.ResponseWriter, r *http.Request) { - body := &struct { - Code string `json:"request_token"` - }{} - - // Try to decode the request body into the struct. If there is an error, - // respond to the client with the error message and a 400 status code. - err := json.NewDecoder(r.Body).Decode(body) - if err != nil { - http.Error(rw, err.Error(), http.StatusBadRequest) - return - } - - tkn := code(body.Code) - - user_info := tkn.userinfo() - - token := map[string]string{ - "user_id": user_info.Sub, - "auth_token": body.Code, - } - - log.Println(token) - rw.Header().Set("Content-Type", "application/json") - json.NewEncoder(rw).Encode(token) -} - -func handleHealth(rw http.ResponseWriter, r *http.Request) { - all_is_well := map[string]string{ - "message": "all's well", - } - json.NewEncoder(rw).Encode(all_is_well) -} - -func handleSignout(rw http.ResponseWriter, r *http.Request) { - rw.WriteHeader(http.StatusFound) -} - -func handleUserinfo(rw http.ResponseWriter, r *http.Request) { - if signed_in == true { - tkn := code(strings.Split(r.Header.Get(AUTH_HEADER), " ")[1]) - user_info := tkn.userinfo() - slsx_userinfo := map[string]map[string]map[string]string{ - "data": { - "user": { - "id": user_info.Sub, - "username": user_info.Name, - "email": user_info.Email, - "firstName": user_info.First_name, - "lastName": user_info.Last_name, - "hicn": user_info.Hicn, - "mbi": user_info.Mbi, - }, - }, - } - signed_in = false - json.NewEncoder(rw).Encode(slsx_userinfo) - } else { - signed_in = true - rw.WriteHeader(http.StatusForbidden) - } -} - -func presentLogin(t *template.Template) http.Handler { - return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - rw.Header().Set("Content-Type", "text/html; charset=utf-8") - r.ParseForm() - t.Execute(rw, r.Form) - }) -} - -func handleLogin(rw http.ResponseWriter, r *http.Request) { - code := login(r) - // redirect with the state, and code - u, err := url.Parse(r.FormValue("redirect_uri")) - if err != nil { - http.Error(rw, err.Error(), http.StatusInternalServerError) - return - } - - q := u.Query() - q.Add("req_token", string(code)) - q.Add("relay", r.FormValue("relay")) - - u.RawQuery = q.Encode() - - http.Redirect(rw, r, u.String(), http.StatusFound) -} - -func login(r *http.Request) code { - usr := r.FormValue(USERNAME_FIELD) - name := r.FormValue(NAME_FIELD) - first_name := r.FormValue(FIRST_NAME_FIELD) - last_name := r.FormValue(LAST_NAME_FIELD) - email := r.FormValue(EMAIL_FIELD) - hicn := r.FormValue(HICN_FIELD) - mbi := r.FormValue(MBI_FIELD) - - return encode(usr, name, first_name, last_name, email, hicn, mbi) -} - -type code string - -func (c code) userinfo() *userinfo { - usr, name, first_name, last_name, email, hicn, mbi := decode(string(c)) - return &userinfo{ - Sub: usr, - Name: name, - First_name: first_name, - Last_name: last_name, - Email: email, - Hicn: hicn, - Mbi: mbi, - } -} - -func decode(c string) (string, string, string, string, string, string, string) { - d_usr, _ := base64.RawURLEncoding.DecodeString(strings.Split(c, ".")[0]) - d_name, _ := base64.RawURLEncoding.DecodeString(strings.Split(c, ".")[1]) - d_first_name, _ := base64.RawURLEncoding.DecodeString(strings.Split(c, ".")[2]) - d_last_name, _ := base64.RawURLEncoding.DecodeString(strings.Split(c, ".")[3]) - d_email, _ := base64.RawURLEncoding.DecodeString(strings.Split(c, ".")[4]) - d_hicn, _ := base64.RawURLEncoding.DecodeString(strings.Split(c, ".")[5]) - d_mbi, _ := base64.RawURLEncoding.DecodeString(strings.Split(c, ".")[6]) - return string(d_usr), string(d_name), string(d_first_name), string(d_last_name), - string(d_email), string(d_hicn), string(d_mbi) -} - -func encode(usr, name, first_name, last_name, email, hicn, mbi string) code { - e_usr := base64.RawURLEncoding.EncodeToString([]byte(usr)) - e_name := base64.RawURLEncoding.EncodeToString([]byte(name)) - e_first_name := base64.RawURLEncoding.EncodeToString([]byte(first_name)) - e_last_name := base64.RawURLEncoding.EncodeToString([]byte(last_name)) - e_email := base64.RawURLEncoding.EncodeToString([]byte(email)) - e_hicn := base64.RawURLEncoding.EncodeToString([]byte(hicn)) - e_mbi := base64.RawURLEncoding.EncodeToString([]byte(mbi)) - return code(fmt.Sprintf("%s.%s.%s.%s.%s.%s.%s", e_usr, e_name, e_first_name, - e_last_name, e_email, e_hicn, e_mbi)) -} - -type userinfo struct { - Sub string `json:"sub"` - Name string `json:"name"` - First_name string `json:"first_name"` - Last_name string `json:"last_name"` - Email string `json:"email"` - Hicn string `json:"hicn"` - Mbi string `json:"mbi"` -} diff --git a/dev-local/mslsx_django/__init__.py b/dev-local/mslsx_django/__init__.py new file mode 100755 index 000000000..e69de29bb diff --git a/dev-local/mslsx_django/settings.py b/dev-local/mslsx_django/settings.py new file mode 100755 index 000000000..1e779961f --- /dev/null +++ b/dev-local/mslsx_django/settings.py @@ -0,0 +1,121 @@ +""" +Django settings for mslsx_django project. + +Generated by 'django-admin startproject' using Django 2.2.13. + +For more information on this file, see +https://docs.djangoproject.com/en/2.2/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/2.2/ref/settings/ +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = '9iq6l*3n7-ljq#wc-fz496#2ri*zdt2skq!4f$!lx@dt8-!t8=' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = ["msls", "localhost", "127.0.0.1"] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'mslsx_django', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'mslsx_django.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'mslsx_django.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/2.2/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + + +# Password validation +# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/2.2/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/2.2/howto/static-files/ + +STATIC_URL = '/static/' diff --git a/dev-local/mslsx_django/templates/login.html b/dev-local/mslsx_django/templates/login.html new file mode 100755 index 000000000..842f3199b --- /dev/null +++ b/dev-local/mslsx_django/templates/login.html @@ -0,0 +1,75 @@ + + + MSLSX Login + + + + +

MSLSX Component Inputs for simulating SLSX/MyMedicare auth flow locally

+

+ BB2 Local Dev Warning +

Simulated Authentication no PHI or PII

+

Use Only Synthetic Beneficiary Info

+

Enter values to be returned by the SLSX user_info endpoint below:

+

+
+
+
+ {% csrf_token %} +
+

Enter User Name:

+ +
+

Enter Identity:

+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + + +
+
+ + diff --git a/dev-local/mslsx_django/urls.py b/dev-local/mslsx_django/urls.py new file mode 100755 index 000000000..bd28a9d12 --- /dev/null +++ b/dev-local/mslsx_django/urls.py @@ -0,0 +1,20 @@ +from django.conf.urls import url + +from .views import ( + login_page, + login, + token, + userinfo, + signout, + health, +) + +urlpatterns = [ + # mslsx end points + url(r'^sso/authorize\\?.+', login_page), + url(r'^login/', login), + url(r'^health', health), + url(r'^sso/session', token), + url(r'^v1/users/', userinfo), + url(r'^sso/signout', signout), +] diff --git a/dev-local/mslsx_django/views.py b/dev-local/mslsx_django/views.py new file mode 100755 index 000000000..fa41e0e05 --- /dev/null +++ b/dev-local/mslsx_django/views.py @@ -0,0 +1,164 @@ +import json +import base64 + +from django.shortcuts import render, HttpResponse, redirect +from django.views.decorators.csrf import csrf_exempt +from urllib.parse import urlencode + + +signed_in = True + +ENCODE_NAME = "ascii" + +ID_FIELD = "id" +USERNAME_FIELD = "username" +NAME_FIELD = "name" +EMAIL_FIELD = "email" +FIRST_NAME_FIELD = "fisrt_name" +LAST_NAME_FIELD = "last_name" +HICN_FIELD = "hicn" +MBI_FIELD = "mbi" +CODE_KEY = "code" +AUTH_HEADER = "Authorization" + + +def _base64_encode(string): + """ + Removes any `=` used as padding from the encoded string. + """ + encoded = base64.urlsafe_b64encode(string) + return encoded.rstrip(b"=") + + +def _base64_decode(string): + """ + Adds back in the required padding before decoding. + """ + padding = 4 - (len(string) % 4) + string = string + ("=" * padding) + return base64.urlsafe_b64decode(string) + + +def _encode(usr="", name="", first_name="", last_name="", email="", hicn="", mbi=""): + return "{}.{}.{}.{}.{}.{}.{}".format(_base64_encode(usr.encode(ENCODE_NAME)).decode(ENCODE_NAME), + _base64_encode(name.encode(ENCODE_NAME)).decode(ENCODE_NAME), + _base64_encode(first_name.encode(ENCODE_NAME)).decode(ENCODE_NAME), + _base64_encode(last_name.encode(ENCODE_NAME)).decode(ENCODE_NAME), + _base64_encode(email.encode(ENCODE_NAME)).decode(ENCODE_NAME), + _base64_encode(hicn.encode(ENCODE_NAME)).decode(ENCODE_NAME), + _base64_encode(mbi.encode(ENCODE_NAME)).decode(ENCODE_NAME)) + + +def _decode(b64code): + flds = b64code.split(".") + return { + "usr": _base64_decode(flds[0]).decode(ENCODE_NAME), + "name": _base64_decode(flds[1]).decode(ENCODE_NAME), + "first_name": _base64_decode(flds[2]).decode(ENCODE_NAME), + "last_name": _base64_decode(flds[3]).decode(ENCODE_NAME), + "email": _base64_decode(flds[4]).decode(ENCODE_NAME), + "hicn": _base64_decode(flds[5]).decode(ENCODE_NAME), + "mbi": _base64_decode(flds[6]).decode(ENCODE_NAME), + } + + +def login_page(request): + ''' + response with login form + ''' + return render(request, 'login.html', { + "relay": request.GET.get("relay", "missing"), + "redirect_uri": request.GET.get("redirect_uri", "missing"), + }) + + +def login(request): + ''' + process login form POST, collect sub, mbi, hicn, etc. + ''' + + redirect_url = request.POST.get("redirect_uri", None) + + req_token = _encode(request.POST.get(USERNAME_FIELD, ""), + request.POST.get(NAME_FIELD, ""), + request.POST.get(FIRST_NAME_FIELD, ""), + request.POST.get(LAST_NAME_FIELD, ""), + request.POST.get(EMAIL_FIELD, ""), + request.POST.get(HICN_FIELD, ""), + request.POST.get(MBI_FIELD, "")) + + qparams = { + "req_token": req_token, + "relay": request.POST.get("relay", "") + } + print("redirect={}?{}".format(redirect_url, urlencode(qparams))) + return redirect("{}?{}".format(redirect_url, urlencode(qparams))) + + +def health(request): + ''' + always good + ''' + return HttpResponse(json.dumps({"message": "all's well"}), + status=200, content_type='application/json') + + +@csrf_exempt +def token(request): + ''' + grant access token to further get userinfo + ''' + body_unicode = request.body.decode('utf-8') + body = json.loads(body_unicode) + request_token = body.get("request_token", None) + + if request_token is None: + return HttpResponse(json.dumps({"message": "Bad Request, missing request token."}), + status=400, content_type='application/json') + + user_info = _decode(request_token) + + token = { + "user_id": user_info['usr'], + "auth_token": request_token, + } + + return HttpResponse(json.dumps(token), status=200, content_type='application/json') + + +def userinfo(request): + + global signed_in + + if signed_in is True: + tkn = request.headers.get(AUTH_HEADER, None) + if tkn is None: + return HttpResponse(json.dumps({"message": "Bad Request, missing request token."}), + status=400, content_type='application/json') + if not tkn.startswith("Bearer "): + return HttpResponse(json.dumps({"message": "Bad Request, malformed bearer token."}), + status=400, content_type='application/json') + tkn = tkn.split()[1] + user_info = _decode(tkn) + slsx_userinfo = { + "data": { + "user": { + "id": user_info["usr"], + "username": user_info["name"], + "email": user_info["email"], + "firstName": user_info["first_name"], + "lastName": user_info["last_name"], + "hicn": user_info["hicn"], + "mbi": user_info["mbi"], + }, + }, + } + signed_in = False + return HttpResponse(json.dumps(slsx_userinfo), status=200, content_type='application/json') + else: + signed_in = True + return HttpResponse(status=403, content_type='application/json') + + +def signout(request): + return HttpResponse(status=302, content_type='application/json') diff --git a/dev-local/mslsx_django/wsgi.py b/dev-local/mslsx_django/wsgi.py new file mode 100755 index 000000000..dea691653 --- /dev/null +++ b/dev-local/mslsx_django/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for mslsx_django project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mslsx_django.settings') + +application = get_wsgi_application() diff --git a/docker-compose.selenium.yml b/docker-compose.selenium.yml index dcc28caa1..9861107b6 100755 --- a/docker-compose.selenium.yml +++ b/docker-compose.selenium.yml @@ -9,6 +9,21 @@ services: environment: - HOSTNAME_URL=${HOSTNAME_URL} - USE_MSLSX=${USE_MSLSX} + - USE_DEBUG=${USE_DEBUG} + volumes: + - .:/code + depends_on: + - bb2slsx + + tests-debug: + build: + context: ./ + dockerfile: Dockerfile.selenium + command: python runtests.py apps.integration_tests.selenium_tests.SeleniumTests + environment: + - HOSTNAME_URL=${HOSTNAME_URL} + - USE_MSLSX=${USE_MSLSX} + - USE_DEBUG=${USE_DEBUG} volumes: - .:/code depends_on: @@ -16,67 +31,28 @@ services: - chrome chrome: - image: selenium/node-chrome:4.0.0-beta-4-prerelease-20210517 - volumes: - - /dev/shm:/dev/shm - depends_on: - - selenium-hub - environment: - - SE_EVENT_BUS_HOST=selenium-hub - - SE_EVENT_BUS_PUBLISH_PORT=4442 - - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 - ports: - - "6900:5900" - - # edge: - # image: selenium/node-edge:4.0.0-beta-4-prerelease-20210517 - # volumes: - # - /dev/shm:/dev/shm - # depends_on: - # - selenium-hub - # environment: - # - SE_EVENT_BUS_HOST=selenium-hub - # - SE_EVENT_BUS_PUBLISH_PORT=4442 - # - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 - # ports: - # - "6901:5900" - - # firefox: - # image: selenium/node-firefox:4.0.0-beta-4-prerelease-20210517 - # volumes: - # - /dev/shm:/dev/shm - # depends_on: - # - selenium-hub - # environment: - # - SE_EVENT_BUS_HOST=selenium-hub - # - SE_EVENT_BUS_PUBLISH_PORT=4442 - # - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 - # ports: - # - "6902:5900" - - selenium-hub: - image: selenium/hub:4.0.0-beta-4-prerelease-20210517 - container_name: selenium-hub + image: selenium/standalone-chrome-debug + hostname: chrome ports: - - "4442:4442" - - "4443:4443" - "4444:4444" + - "5900:5900" msls: - build: - context: ./dev-local/msls - dockerfile: Dockerfile - command: msls + build: . + command: bash -c "cd dev-local ; python manage.py migrate ; python3 -m debugpy --listen 0.0.0.0:7890 manage.py runserver 0.0.0.0:8080 --noreload" + environment: + - DJANGO_SETTINGS_MODULE=mslsx_django.settings ports: - - "8080:8080" - + - "8080:8080" + - "7890:7890" + db: image: postgres environment: - - POSTGRES_DB=bluebutton - - POSTGRES_PASSWORD=toor + - POSTGRES_DB=bluebutton + - POSTGRES_PASSWORD=toor ports: - - "5432:5432" + - "5432:5432" bb2slsx: build: . diff --git a/docker-compose.yml b/docker-compose.yml index 5d1d7f318..0d43ba406 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,12 +2,13 @@ version: '3' services: msls: - build: - context: ./dev-local/msls - dockerfile: Dockerfile - command: msls + build: . + command: bash -c "cd dev-local ; python manage.py migrate ; python3 -m debugpy --listen 0.0.0.0:7890 manage.py runserver 0.0.0.0:8080 --noreload" + environment: + - DJANGO_SETTINGS_MODULE=mslsx_django.settings ports: - "8080:8080" + - "7890:7890" db: image: postgres environment: diff --git a/docker-compose/readme.md b/docker-compose/readme.md index 967b5ea83..7e876abd3 100644 --- a/docker-compose/readme.md +++ b/docker-compose/readme.md @@ -349,15 +349,20 @@ You can run selenium tests by following below steps: ./docker-compose/run_selenium_tests_local_keybase.sh slsx ``` - 3. To trouble shoot tests (visualize webUI interaction): point VNC client to localhost:6900 + 3. To debug tests (visualize webUI interaction): point VNC client to localhost:5900 1. requires installation of vnc viewer, password (secret) - 2. also need to comment out webdriver headless option, as shown below: - ``` - ./apps/integration_tests/selenium_tests.py: setUp(): - ... - opt = webdriver.ChromeOptions() - opt.add_argument('--headless') - opt.add_argument("--disable-dev-shm-usage") - ... - ``` + 2. Start tests using -debug parameter as shown below: + use MSLSX (default) + ``` + ./docker-compose/run_selenium_tests_local_keybase.sh debug + ``` + + ``` + ./docker-compose/run_selenium_tests_local_keybase.sh mslsx-debug + ``` + + use SLSX + ``` + ./docker-compose/run_selenium_tests_local_keybase.sh slsx-debug + ``` diff --git a/docker-compose/run_selenium_tests_local_keybase.sh b/docker-compose/run_selenium_tests_local_keybase.sh index 7208e4d63..6d597c4d0 100755 --- a/docker-compose/run_selenium_tests_local_keybase.sh +++ b/docker-compose/run_selenium_tests_local_keybase.sh @@ -13,7 +13,9 @@ KEYBASE_ENV_FILE="team/bb20/infrastructure/creds/ENV_secrets_for_local_integration_tests.env" KEYBASE_CERTFILES_SUBPATH="team/bb20/infrastructure/certs/local_integration_tests/fhir_client/certstore/" -CERTSTORE_TEMPORARY_MOUNT_PATH="./docker-compose/certstore" +export CERTSTORE_TEMPORARY_MOUNT_PATH="./docker-compose/certstore" +export DJANGO_FHIR_CERTSTORE="/code/docker-compose/certstore" + CERT_FILENAME="client_data_server_bluebutton_local_integration_tests_certificate.pem" KEY_FILENAME="client_data_server_bluebutton_local_integration_tests_private_key.pem" @@ -43,6 +45,7 @@ set -e -u -o pipefail TEST_TYPE="--selenium" export USE_MSLSX=true +export USE_DEBUG=false export DJANGO_SETTINGS_MODULE="hhs_oauth_server.settings.dev" export DJANGO_FHIR_CERTSTORE="/code/docker-compose/certstore" @@ -58,7 +61,7 @@ if [ $# -eq 0 ] then echo "Use MSLSX for identity service." else - if [[ $1 != "slsx" && $1 != "mslsx" && $1 != "logit" ]] + if [[ $1 != "slsx" && $1 != "mslsx" && $1 != "slsx-debug" && $1 != "mslsx-debug" && $1 != "debug" && $1 != "logit" ]] then echo echo "COMMAND USAGE HELP" @@ -66,15 +69,25 @@ else echo echo " Use one of the following command line options for the type of test to run:" echo - echo " logit = run integration tests for bb2 loggings, MSLSX used as identity service." + echo " slsx = use SLSX for identity service with webdriver in headless mode." + echo + echo " slsx-debug = use SLSX for identity service, and start selenium standalone chrome debug mode (visualized browser interactions)." + echo + echo " mslsx (default) = use MSLSX for identity service with webdriver in headless mode." + echo + echo " mslsx-debug = use MSLSX for identity service with selenium chrome standalone debug mode." echo - echo " slsx = use SLSX for identity service." + echo " debug = same as 'mslsx-debug'" echo - echo " mslsx (default) = use MSLSX for identity service." + echo " logit = run integration tests for bb2 loggings, MSLSX used as identity service." echo exit 1 else - if [ $1 == "slsx" ] + if [[ $1 == *debug ]] + then + export USE_DEBUG=true + fi + if [[ $1 == "slsx" || $1 == "slsx-debug" ]] then export USE_MSLSX=false export DJANGO_MEDICARE_SLSX_REDIRECT_URI="http://bb2slsx:8000/mymedicare/sls-callback" @@ -84,7 +97,7 @@ else export DJANGO_SLSX_SIGNOUT_ENDPOINT="https://test.medicare.gov/sso/signout" export DJANGO_SLSX_USERINFO_ENDPOINT="https://test.accounts.cms.gov/v1/users" fi - if [ $1 == "logit" ] + if [[ $1 == "logit" ]] then TEST_TYPE="--logit" TESTS_LIST="apps.integration_tests.logging_tests.LoggingTests.test_auth_fhir_flows_logging" @@ -205,8 +218,15 @@ export DJANGO_SLSX_CLIENT_ID=${DJANGO_SLSX_CLIENT_ID} export DJANGO_SLSX_CLIENT_SECRET=${DJANGO_SLSX_CLIENT_SECRET} echo "Selenium tests ..." +echo "MSLSX=" ${USE_MSLSX} +echo "DEBUG=" ${USE_DEBUG} -docker-compose -f docker-compose.selenium.yml run tests bash -c "python runtests.py ${TEST_TYPE} ${TESTS_LIST}" +if [ "$USE_DEBUG" = true ] +then + docker-compose -f docker-compose.selenium.yml run tests-debug bash -c "python runtests.py ${TEST_TYPE} ${TESTS_LIST}" +else + docker-compose -f docker-compose.selenium.yml run tests bash -c "python runtests.py ${TEST_TYPE} ${TESTS_LIST}" +fi # Stop containers after use echo_msg diff --git a/hhs_oauth_server/settings/test_logging.py b/hhs_oauth_server/settings/test_logging.py index d90642520..90674693f 100755 --- a/hhs_oauth_server/settings/test_logging.py +++ b/hhs_oauth_server/settings/test_logging.py @@ -7,14 +7,16 @@ 'filename': '/code/docker-compose/tmp/bb2_logging_test.log', } -if LOGGING.get('loggers'): +loggers = LOGGING.get('loggers') + +if loggers: # Update hhs_server logger - LOGGING.get['loggers']['hhs_server'] = { + loggers['hhs_server'] = { 'handlers': ['console', 'file'], 'level': 'INFO', } # Update audit logger - LOGGING['loggers']['audit'] = { + loggers['audit'] = { 'handlers': ['console', 'file'], 'level': 'INFO', } diff --git a/runtests.py b/runtests.py index 336589bfa..291630c06 100644 --- a/runtests.py +++ b/runtests.py @@ -50,7 +50,7 @@ # Unset ENV variables for integration type tests so default values get set. for env_var in ['DJANGO_MEDICARE_SLSX_LOGIN_URI', 'DJANGO_MEDICARE_SLSX_REDIRECT_URI', 'DJANGO_SLSX_USERINFO_ENDPOINT', 'DJANGO_SLSX_TOKEN_ENDPOINT', - 'DJANGO_SLSX_HEALTH_CHECK_ENDPOINT', + 'DJANGO_SLSX_HEALTH_CHECK_ENDPOINT', "DJANGO_SLSX_SIGNOUT_ENDPOINT", 'DATABASES_CUSTOM', 'DJANGO_LOG_JSON_FORMAT_PRETTY']: if env_var in os.environ: del os.environ[env_var] @@ -59,7 +59,7 @@ # Unset ENV variables for Django unit type tests so default values get set. for env_var in ['FHIR_URL', 'DJANGO_MEDICARE_SLSX_LOGIN_URI', 'DJANGO_MEDICARE_SLSX_REDIRECT_URI', 'DJANGO_SLSX_USERINFO_ENDPOINT', 'DJANGO_SLSX_TOKEN_ENDPOINT', - 'DJANGO_SLSX_HEALTH_CHECK_ENDPOINT', + 'DJANGO_SLSX_HEALTH_CHECK_ENDPOINT', "DJANGO_SLSX_SIGNOUT_ENDPOINT", 'DJANGO_FHIR_CERTSTORE', 'DATABASES_CUSTOM', 'DJANGO_LOG_JSON_FORMAT_PRETTY', 'DJANGO_USER_ID_ITERATIONS', 'DJANGO_USER_ID_SALT']: if env_var in os.environ: From c53cfb3a34f9600007f6ef3d4ba268cbe34f2d2d Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Mon, 30 Aug 2021 16:02:43 -0700 Subject: [PATCH 10/39] fix linting. --- apps/integration_tests/integration_test_fhir_resources.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/integration_tests/integration_test_fhir_resources.py b/apps/integration_tests/integration_test_fhir_resources.py index 5a4bd9e97..25e4a5717 100644 --- a/apps/integration_tests/integration_test_fhir_resources.py +++ b/apps/integration_tests/integration_test_fhir_resources.py @@ -1,4 +1,5 @@ import json +import os from django.conf import settings from django.contrib.staticfiles.testing import StaticLiveServerTestCase From 807cea4befb68f3cf881e0dc2dd1f915a1d63065 Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Tue, 31 Aug 2021 10:58:59 -0700 Subject: [PATCH 11/39] simplify audit logging redirect. --- apps/integration_tests/logging_tests.py | 8 ++++++++ hhs_oauth_server/settings/test_logging.py | 22 ---------------------- runtests.py | 4 ++-- 3 files changed, 10 insertions(+), 24 deletions(-) delete mode 100755 hhs_oauth_server/settings/test_logging.py diff --git a/apps/integration_tests/logging_tests.py b/apps/integration_tests/logging_tests.py index 65d662af7..af234c562 100755 --- a/apps/integration_tests/logging_tests.py +++ b/apps/integration_tests/logging_tests.py @@ -1,6 +1,7 @@ import copy import json import re +import logging from json.decoder import JSONDecodeError @@ -196,6 +197,13 @@ def _validate_events(self): self.assertEqual(len(expected_events), 0) def test_auth_fhir_flows_logging(self): + # direct relevant log records to the file + audit_logger = logging.getLogger("audit") + file_handler = logging.FileHandler(TEST_LOGGING_FILE) + for h in audit_logger.handlers[:]: + audit_logger.removeHandler(h) + audit_logger.addHandler(file_handler) + audit_logger.setLevel(logging.INFO) self.test_auth_grant_fhir_calls_v1() print("validating logging events in log...") self._validate_events() diff --git a/hhs_oauth_server/settings/test_logging.py b/hhs_oauth_server/settings/test_logging.py deleted file mode 100755 index 90674693f..000000000 --- a/hhs_oauth_server/settings/test_logging.py +++ /dev/null @@ -1,22 +0,0 @@ -from .dev import * - -# Use env-specific logging config if present -# Add file handler. -LOGGING['handlers']['file'] = { - 'class': 'logging.FileHandler', - 'filename': '/code/docker-compose/tmp/bb2_logging_test.log', -} - -loggers = LOGGING.get('loggers') - -if loggers: - # Update hhs_server logger - loggers['hhs_server'] = { - 'handlers': ['console', 'file'], - 'level': 'INFO', - } - # Update audit logger - loggers['audit'] = { - 'handlers': ['console', 'file'], - 'level': 'INFO', - } diff --git a/runtests.py b/runtests.py index 291630c06..be2401bbf 100644 --- a/runtests.py +++ b/runtests.py @@ -66,10 +66,10 @@ del os.environ[env_var] if __name__ == '__main__': - if not args.logit: + if not args.selenium and not args.logit: os.environ['DJANGO_SETTINGS_MODULE'] = 'hhs_oauth_server.settings.test' else: - os.environ['DJANGO_SETTINGS_MODULE'] = 'hhs_oauth_server.settings.test_logging' + os.environ['DJANGO_SETTINGS_MODULE'] = 'hhs_oauth_server.settings.dev' django.setup() TestRunner = get_runner(settings) test_runner = TestRunner() From 2ac4a3fc9fa99c114cecfff4b827e96e76c7ff30 Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Wed, 1 Sep 2021 15:47:29 -0700 Subject: [PATCH 12/39] cleanup. --- apps/integration_tests/logging_tests.py | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/integration_tests/logging_tests.py b/apps/integration_tests/logging_tests.py index af234c562..f492b06ee 100755 --- a/apps/integration_tests/logging_tests.py +++ b/apps/integration_tests/logging_tests.py @@ -197,7 +197,6 @@ def _validate_events(self): self.assertEqual(len(expected_events), 0) def test_auth_fhir_flows_logging(self): - # direct relevant log records to the file audit_logger = logging.getLogger("audit") file_handler = logging.FileHandler(TEST_LOGGING_FILE) for h in audit_logger.handlers[:]: From 77e95457fb93c7009fd6f6b4cee4c751f8430490 Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Fri, 3 Sep 2021 20:35:42 -0700 Subject: [PATCH 13/39] add logging integration tests to cbc job pipeline. --- ...insfile.cbc-run-multi-pr-checks-w-selenium | 16 +++++++++++-- apps/integration_tests/logging_tests.py | 7 ------ .../run_selenium_tests_local_keybase.sh | 24 +++++++++---------- hhs_oauth_server/settings/logging_it.py | 20 ++++++++++++++++ runtests.py | 11 ++------- 5 files changed, 47 insertions(+), 31 deletions(-) create mode 100755 hhs_oauth_server/settings/logging_it.py diff --git a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium index 2348dd012..4849e9169 100755 --- a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium +++ b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium @@ -11,7 +11,7 @@ pipeline { USE_DEBUG = false DJANGO_SLSX_VERIFY_SSL_INTERNAL = false DJANGO_SLSX_VERIFY_SSL_EXTERNAL = false - DJANGO_LOG_JSON_FORMAT_PRETTY = true + DJANGO_LOG_JSON_FORMAT_PRETTY = false DJANGO_SETTINGS_MODULE = "hhs_oauth_server.settings.dev" OAUTHLIB_INSECURE_TRANSPORT = true DJANGO_SECURE_SESSION = false @@ -100,7 +100,19 @@ pipeline { steps{ sh """ . venv/bin/activate - export DJANGO_SETTINGS_MODULE=hhs_oauth_server.settings.dev && python manage.py migrate && python manage.py create_admin_groups && python manage.py loaddata scopes.json && python manage.py create_blue_button_scopes && python manage.py create_test_user_and_application && python manage.py create_user_identification_label_selection && python manage.py create_test_feature_switches && (if [ ! -d 'bluebutton-css' ] ; then git clone https://github.com/CMSgov/bluebutton-css.git ; else echo 'CSS already installed.' ; fi) && echo 'starting bb2...' && (python manage.py runserver 0.0.0.0:8000 &) + export DJANGO_SETTINGS_MODULE=hhs_oauth_server.settings.logging_it && python manage.py migrate && python manage.py create_admin_groups && python manage.py loaddata scopes.json && python manage.py create_blue_button_scopes && python manage.py create_test_user_and_application && python manage.py create_user_identification_label_selection && python manage.py create_test_feature_switches && (if [ ! -d 'bluebutton-css' ] ; then git clone https://github.com/CMSgov/bluebutton-css.git ; else echo 'CSS already installed.' ; fi) && echo 'starting bb2...' && (python manage.py runserver 0.0.0.0:8000 &) + """ + } + } + + stage("RUN logging integration tests") { + when { + expression { params.RUN_SELENIUM_TESTS == true } + } + steps{ + sh """ + . venv/bin/activate + python runtests.py --selenium apps.integration_tests.logging_tests.LoggingTests.test_auth_fhir_flows_logging """ } } diff --git a/apps/integration_tests/logging_tests.py b/apps/integration_tests/logging_tests.py index f492b06ee..65d662af7 100755 --- a/apps/integration_tests/logging_tests.py +++ b/apps/integration_tests/logging_tests.py @@ -1,7 +1,6 @@ import copy import json import re -import logging from json.decoder import JSONDecodeError @@ -197,12 +196,6 @@ def _validate_events(self): self.assertEqual(len(expected_events), 0) def test_auth_fhir_flows_logging(self): - audit_logger = logging.getLogger("audit") - file_handler = logging.FileHandler(TEST_LOGGING_FILE) - for h in audit_logger.handlers[:]: - audit_logger.removeHandler(h) - audit_logger.addHandler(file_handler) - audit_logger.setLevel(logging.INFO) self.test_auth_grant_fhir_calls_v1() print("validating logging events in log...") self._validate_events() diff --git a/docker-compose/run_selenium_tests_local_keybase.sh b/docker-compose/run_selenium_tests_local_keybase.sh index 6d597c4d0..9907c2a53 100755 --- a/docker-compose/run_selenium_tests_local_keybase.sh +++ b/docker-compose/run_selenium_tests_local_keybase.sh @@ -42,8 +42,6 @@ echo_msg # Set bash builtins for safety set -e -u -o pipefail -TEST_TYPE="--selenium" - export USE_MSLSX=true export USE_DEBUG=false export DJANGO_SETTINGS_MODULE="hhs_oauth_server.settings.dev" @@ -99,9 +97,8 @@ else fi if [[ $1 == "logit" ]] then - TEST_TYPE="--logit" TESTS_LIST="apps.integration_tests.logging_tests.LoggingTests.test_auth_fhir_flows_logging" - export DJANGO_SETTINGS_MODULE="hhs_oauth_server.settings.test_logging" + export DJANGO_SETTINGS_MODULE="hhs_oauth_server.settings.logging_it" export DJANGO_LOG_JSON_FORMAT_PRETTY=False mkdir -p docker-compose/tmp if [ -f docker-compose/tmp/bb2_logging_test.log ] @@ -114,7 +111,6 @@ fi echo "DJANGO_SETTINGS_MODULE: " ${DJANGO_SETTINGS_MODULE} echo "HOSTNAME_URL: " ${HOSTNAME_URL} -echo "TEST_TYPE: " ${TEST_TYPE} echo "TESTS: " ${TESTS_LIST} # Set KeyBase ENV path based on your type of system @@ -217,17 +213,19 @@ export DJANGO_PASSWORD_HASH_ITERATIONS=${DJANGO_PASSWORD_HASH_ITERATIONS} export DJANGO_SLSX_CLIENT_ID=${DJANGO_SLSX_CLIENT_ID} export DJANGO_SLSX_CLIENT_SECRET=${DJANGO_SLSX_CLIENT_SECRET} -echo "Selenium tests ..." -echo "MSLSX=" ${USE_MSLSX} -echo "DEBUG=" ${USE_DEBUG} - -if [ "$USE_DEBUG" = true ] +TEST_SERVICE_NAME="tests" +if [[ "$USE_DEBUG" = true ]] then - docker-compose -f docker-compose.selenium.yml run tests-debug bash -c "python runtests.py ${TEST_TYPE} ${TESTS_LIST}" -else - docker-compose -f docker-compose.selenium.yml run tests bash -c "python runtests.py ${TEST_TYPE} ${TESTS_LIST}" + TEST_SERVICE_NAME="tests-debug" fi +echo "Selenium tests ..." +echo "MSLSX =" ${USE_MSLSX} +echo "DEBUG =" ${USE_DEBUG} +echo "DOCKER COMPOSE TEST SERVICE NAME =" ${TEST_SERVICE_NAME} + +docker-compose -f docker-compose.selenium.yml run ${TEST_SERVICE_NAME} bash -c "python runtests.py --selenium ${TESTS_LIST}" + # Stop containers after use echo_msg echo_msg "Stopping containers..." diff --git a/hhs_oauth_server/settings/logging_it.py b/hhs_oauth_server/settings/logging_it.py new file mode 100755 index 000000000..9b0b328f5 --- /dev/null +++ b/hhs_oauth_server/settings/logging_it.py @@ -0,0 +1,20 @@ +from .dev import * + +# Override audit logging handler with a file handler +logging_handlers = LOGGING['handlers'] + +if logging_handlers is None: + raise ValueError("Bad settings, expecting handlers defined in settings.LOGGING") + +logging_handlers['file'] = {'class': 'logging.FileHandler', + 'filename': '/code/docker-compose/tmp/bb2_logging_test.log', } + +loggers = LOGGING.get('loggers') + +if loggers: + logging_logger_audit_handlers = LOGGING['loggers']['audit']['handlers'] + + if logging_logger_audit_handlers is None: + raise ValueError("Bad settings, expecting handlers defined in settings.LOGGING for 'audit' logger") + + logging_logger_audit_handlers.append('file') diff --git a/runtests.py b/runtests.py index be2401bbf..1eafd0402 100644 --- a/runtests.py +++ b/runtests.py @@ -18,9 +18,6 @@ --selenium This optional flag indicates tests are to run in selenium test mode. Space separated list of Django tests to run. - --logit This optional flag indicates tests are to run in logging integration test mode. - Space separated list of Django tests to run. - For example: $ docker-compose exec web python runtests.py apps.dot_ext.tests @@ -42,7 +39,6 @@ parser = argparse.ArgumentParser() parser.add_argument('--integration', help='Integration tests mode', action='store_true') parser.add_argument('--selenium', help='Selenium tests mode', action='store_true') -parser.add_argument('--logit', help='Logging tests mode (use selenium as driver)', action='store_true') parser.add_argument('test', nargs='*') args = parser.parse_args() @@ -54,7 +50,7 @@ 'DATABASES_CUSTOM', 'DJANGO_LOG_JSON_FORMAT_PRETTY']: if env_var in os.environ: del os.environ[env_var] -elif not args.selenium and not args.logit: +elif not args.selenium: # For tests using selenium, inherit SLSX config from context; # Unset ENV variables for Django unit type tests so default values get set. for env_var in ['FHIR_URL', 'DJANGO_MEDICARE_SLSX_LOGIN_URI', 'DJANGO_MEDICARE_SLSX_REDIRECT_URI', @@ -66,10 +62,7 @@ del os.environ[env_var] if __name__ == '__main__': - if not args.selenium and not args.logit: - os.environ['DJANGO_SETTINGS_MODULE'] = 'hhs_oauth_server.settings.test' - else: - os.environ['DJANGO_SETTINGS_MODULE'] = 'hhs_oauth_server.settings.dev' + os.environ['DJANGO_SETTINGS_MODULE'] = 'hhs_oauth_server.settings.test' django.setup() TestRunner = get_runner(settings) test_runner = TestRunner() From eea26f9b42c8e7240bff8043c739af390e761521 Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Fri, 3 Sep 2021 20:59:17 -0700 Subject: [PATCH 14/39] fix log file path. --- hhs_oauth_server/settings/logging_it.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hhs_oauth_server/settings/logging_it.py b/hhs_oauth_server/settings/logging_it.py index 9b0b328f5..4117e60b3 100755 --- a/hhs_oauth_server/settings/logging_it.py +++ b/hhs_oauth_server/settings/logging_it.py @@ -7,7 +7,7 @@ raise ValueError("Bad settings, expecting handlers defined in settings.LOGGING") logging_handlers['file'] = {'class': 'logging.FileHandler', - 'filename': '/code/docker-compose/tmp/bb2_logging_test.log', } + 'filename': './docker-compose/tmp/bb2_logging_test.log', } loggers = LOGGING.get('loggers') From b8f649079f5ff937174c20db9ae9072897bd6463 Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Fri, 3 Sep 2021 21:12:09 -0700 Subject: [PATCH 15/39] remove print. --- dev-local/mslsx_django/views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/dev-local/mslsx_django/views.py b/dev-local/mslsx_django/views.py index fa41e0e05..c59745cd3 100755 --- a/dev-local/mslsx_django/views.py +++ b/dev-local/mslsx_django/views.py @@ -91,7 +91,6 @@ def login(request): "req_token": req_token, "relay": request.POST.get("relay", "") } - print("redirect={}?{}".format(redirect_url, urlencode(qparams))) return redirect("{}?{}".format(redirect_url, urlencode(qparams))) From 6b9a98abdff7fbad1d7fca43c74759fca1817faf Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Fri, 3 Sep 2021 21:31:55 -0700 Subject: [PATCH 16/39] create audit logger file handler path. --- Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium index 4849e9169..881a13f95 100755 --- a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium +++ b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium @@ -100,7 +100,7 @@ pipeline { steps{ sh """ . venv/bin/activate - export DJANGO_SETTINGS_MODULE=hhs_oauth_server.settings.logging_it && python manage.py migrate && python manage.py create_admin_groups && python manage.py loaddata scopes.json && python manage.py create_blue_button_scopes && python manage.py create_test_user_and_application && python manage.py create_user_identification_label_selection && python manage.py create_test_feature_switches && (if [ ! -d 'bluebutton-css' ] ; then git clone https://github.com/CMSgov/bluebutton-css.git ; else echo 'CSS already installed.' ; fi) && echo 'starting bb2...' && (python manage.py runserver 0.0.0.0:8000 &) + export DJANGO_SETTINGS_MODULE=hhs_oauth_server.settings.logging_it && mkdir -p docker-compose/tmp && python manage.py migrate && python manage.py create_admin_groups && python manage.py loaddata scopes.json && python manage.py create_blue_button_scopes && python manage.py create_test_user_and_application && python manage.py create_user_identification_label_selection && python manage.py create_test_feature_switches && (if [ ! -d 'bluebutton-css' ] ; then git clone https://github.com/CMSgov/bluebutton-css.git ; else echo 'CSS already installed.' ; fi) && echo 'starting bb2...' && (python manage.py runserver 0.0.0.0:8000 &) """ } } From d3d2a1f868c94f28ff0c7e61b085c9e846fbf3ba Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Fri, 3 Sep 2021 22:47:53 -0700 Subject: [PATCH 17/39] trace logging. --- Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium | 1 + 1 file changed, 1 insertion(+) diff --git a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium index 881a13f95..7570b8411 100755 --- a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium +++ b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium @@ -112,6 +112,7 @@ pipeline { steps{ sh """ . venv/bin/activate + ls -al ./docker-compose/tmp python runtests.py --selenium apps.integration_tests.logging_tests.LoggingTests.test_auth_fhir_flows_logging """ } From 6c0c2d72bd8e692611da4f798c63af89f649545c Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Fri, 3 Sep 2021 23:26:27 -0700 Subject: [PATCH 18/39] trace --- Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium | 1 + 1 file changed, 1 insertion(+) diff --git a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium index 7570b8411..a7641a9ba 100755 --- a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium +++ b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium @@ -113,6 +113,7 @@ pipeline { sh """ . venv/bin/activate ls -al ./docker-compose/tmp + find . -name "bb2_logging_test.log" python runtests.py --selenium apps.integration_tests.logging_tests.LoggingTests.test_auth_fhir_flows_logging """ } From f123d1f50688c7d364fceb354dda3b6c3686155f Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Fri, 3 Sep 2021 23:46:05 -0700 Subject: [PATCH 19/39] trace --- Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium index a7641a9ba..cb5dc58f0 100755 --- a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium +++ b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium @@ -100,7 +100,8 @@ pipeline { steps{ sh """ . venv/bin/activate - export DJANGO_SETTINGS_MODULE=hhs_oauth_server.settings.logging_it && mkdir -p docker-compose/tmp && python manage.py migrate && python manage.py create_admin_groups && python manage.py loaddata scopes.json && python manage.py create_blue_button_scopes && python manage.py create_test_user_and_application && python manage.py create_user_identification_label_selection && python manage.py create_test_feature_switches && (if [ ! -d 'bluebutton-css' ] ; then git clone https://github.com/CMSgov/bluebutton-css.git ; else echo 'CSS already installed.' ; fi) && echo 'starting bb2...' && (python manage.py runserver 0.0.0.0:8000 &) + pwd + export DJANGO_SETTINGS_MODULE=hhs_oauth_server.settings.logging_it && mkdir -p docker-compose/tmp && python manage.py migrate && python manage.py create_admin_groups && python manage.py loaddata scopes.json && python manage.py create_blue_button_scopes && python manage.py create_test_user_and_application && python manage.py create_user_identification_label_selection && python manage.py create_test_feature_switches && (if [ ! -d 'bluebutton-css' ] ; then git clone https://github.com/CMSgov/bluebutton-css.git ; else echo 'CSS already installed.' ; fi) && echo 'starting bb2...' && (python manage.py runserver 0.0.0.0:8000 > bb2.log 2>&1 &) """ } } @@ -112,8 +113,11 @@ pipeline { steps{ sh """ . venv/bin/activate + ls -al . ls -al ./docker-compose/tmp find . -name "bb2_logging_test.log" + pwd + cat ./bb2.log python runtests.py --selenium apps.integration_tests.logging_tests.LoggingTests.test_auth_fhir_flows_logging """ } From 8b00a95ea9cea5ff94247c3b20321a43523acce8 Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Sat, 4 Sep 2021 10:00:41 -0700 Subject: [PATCH 20/39] fix file handler. --- hhs_oauth_server/settings/logging_it.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hhs_oauth_server/settings/logging_it.py b/hhs_oauth_server/settings/logging_it.py index 4117e60b3..1c77336a1 100755 --- a/hhs_oauth_server/settings/logging_it.py +++ b/hhs_oauth_server/settings/logging_it.py @@ -3,11 +3,13 @@ # Override audit logging handler with a file handler logging_handlers = LOGGING['handlers'] +logfile_path = os.path.abspath(os.getcwd() + "/docker-compose/tmp/bb2_logging_test.log") + if logging_handlers is None: raise ValueError("Bad settings, expecting handlers defined in settings.LOGGING") logging_handlers['file'] = {'class': 'logging.FileHandler', - 'filename': './docker-compose/tmp/bb2_logging_test.log', } + 'filename': logfile_path, } loggers = LOGGING.get('loggers') From bc2d352f2008c0b688934bd4ff2b1f77b5959481 Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Sat, 4 Sep 2021 11:53:24 -0700 Subject: [PATCH 21/39] fix logger file handler. --- .../Jenkinsfile.cbc-run-multi-pr-checks-w-selenium | 8 +------- apps/integration_tests/logging_tests.py | 1 - docker-compose/run_selenium_tests_local_keybase.sh | 5 ----- hhs_oauth_server/settings/logging_it.py | 12 ++++++++++++ 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium index cb5dc58f0..4849e9169 100755 --- a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium +++ b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium @@ -100,8 +100,7 @@ pipeline { steps{ sh """ . venv/bin/activate - pwd - export DJANGO_SETTINGS_MODULE=hhs_oauth_server.settings.logging_it && mkdir -p docker-compose/tmp && python manage.py migrate && python manage.py create_admin_groups && python manage.py loaddata scopes.json && python manage.py create_blue_button_scopes && python manage.py create_test_user_and_application && python manage.py create_user_identification_label_selection && python manage.py create_test_feature_switches && (if [ ! -d 'bluebutton-css' ] ; then git clone https://github.com/CMSgov/bluebutton-css.git ; else echo 'CSS already installed.' ; fi) && echo 'starting bb2...' && (python manage.py runserver 0.0.0.0:8000 > bb2.log 2>&1 &) + export DJANGO_SETTINGS_MODULE=hhs_oauth_server.settings.logging_it && python manage.py migrate && python manage.py create_admin_groups && python manage.py loaddata scopes.json && python manage.py create_blue_button_scopes && python manage.py create_test_user_and_application && python manage.py create_user_identification_label_selection && python manage.py create_test_feature_switches && (if [ ! -d 'bluebutton-css' ] ; then git clone https://github.com/CMSgov/bluebutton-css.git ; else echo 'CSS already installed.' ; fi) && echo 'starting bb2...' && (python manage.py runserver 0.0.0.0:8000 &) """ } } @@ -113,11 +112,6 @@ pipeline { steps{ sh """ . venv/bin/activate - ls -al . - ls -al ./docker-compose/tmp - find . -name "bb2_logging_test.log" - pwd - cat ./bb2.log python runtests.py --selenium apps.integration_tests.logging_tests.LoggingTests.test_auth_fhir_flows_logging """ } diff --git a/apps/integration_tests/logging_tests.py b/apps/integration_tests/logging_tests.py index 65d662af7..1da54aad9 100755 --- a/apps/integration_tests/logging_tests.py +++ b/apps/integration_tests/logging_tests.py @@ -167,7 +167,6 @@ class LoggingTests(SeleniumTests): the driver (selenium) ''' def _validate_events(self): - # validate middleware logging records in log file ./docker-compose/tmp/bb2_logging_test.log with open(TEST_LOGGING_FILE, 'r') as f: log_records = f.readlines() start_validation = False diff --git a/docker-compose/run_selenium_tests_local_keybase.sh b/docker-compose/run_selenium_tests_local_keybase.sh index 9907c2a53..6b2774234 100755 --- a/docker-compose/run_selenium_tests_local_keybase.sh +++ b/docker-compose/run_selenium_tests_local_keybase.sh @@ -100,11 +100,6 @@ else TESTS_LIST="apps.integration_tests.logging_tests.LoggingTests.test_auth_fhir_flows_logging" export DJANGO_SETTINGS_MODULE="hhs_oauth_server.settings.logging_it" export DJANGO_LOG_JSON_FORMAT_PRETTY=False - mkdir -p docker-compose/tmp - if [ -f docker-compose/tmp/bb2_logging_test.log ] - then - rm -f docker-compose/tmp/bb2_logging_test.log - fi fi fi fi diff --git a/hhs_oauth_server/settings/logging_it.py b/hhs_oauth_server/settings/logging_it.py index 1c77336a1..bd88a1da9 100755 --- a/hhs_oauth_server/settings/logging_it.py +++ b/hhs_oauth_server/settings/logging_it.py @@ -4,6 +4,18 @@ logging_handlers = LOGGING['handlers'] logfile_path = os.path.abspath(os.getcwd() + "/docker-compose/tmp/bb2_logging_test.log") +logdir_path = os.path.abspath(os.getcwd() + "/docker-compose/tmp/") + +try: + # remove left over just in case + os.remove(logfile_path) +except FileNotFoundError as err: + print("Error removing left over log file: {}".format(err)) + +try: + os.mkdir(logdir_path) +except FileExistsError as err: + print("Error os.mkdir: {}".format(err)) if logging_handlers is None: raise ValueError("Bad settings, expecting handlers defined in settings.LOGGING") From e8ef516eceb94d4b4014deaa7b880614abd59779 Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Sat, 4 Sep 2021 12:20:47 -0700 Subject: [PATCH 22/39] temp comment out logging integration tests from pipeline. --- ...insfile.cbc-run-multi-pr-checks-w-selenium | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium index 4849e9169..400921c17 100755 --- a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium +++ b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium @@ -105,17 +105,17 @@ pipeline { } } - stage("RUN logging integration tests") { - when { - expression { params.RUN_SELENIUM_TESTS == true } - } - steps{ - sh """ - . venv/bin/activate - python runtests.py --selenium apps.integration_tests.logging_tests.LoggingTests.test_auth_fhir_flows_logging - """ - } - } + // stage("RUN logging integration tests") { + // when { + // expression { params.RUN_SELENIUM_TESTS == true } + // } + // steps{ + // sh """ + // . venv/bin/activate + // python runtests.py --selenium apps.integration_tests.logging_tests.LoggingTests.test_auth_fhir_flows_logging + // """ + // } + // } stage("RUN integration tests") { steps{ From 52e3c8cdf72471003d0257154b15be03f3e4de02 Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Sat, 4 Sep 2021 13:04:51 -0700 Subject: [PATCH 23/39] trace --- hhs_oauth_server/settings/logging_it.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hhs_oauth_server/settings/logging_it.py b/hhs_oauth_server/settings/logging_it.py index bd88a1da9..77549ed33 100755 --- a/hhs_oauth_server/settings/logging_it.py +++ b/hhs_oauth_server/settings/logging_it.py @@ -31,4 +31,10 @@ if logging_logger_audit_handlers is None: raise ValueError("Bad settings, expecting handlers defined in settings.LOGGING for 'audit' logger") + print("------------ append file handler------------") logging_logger_audit_handlers.append('file') + print("------------ LOGGING begin ------------") + print("{}".format(LOGGING)) + print("------------ LOGGING end ------------") +else: + print("------------ no loggers found ------------") From 7d8196fe8d0438bb2a6b4799e53c9524e349ecd6 Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Sat, 4 Sep 2021 13:35:39 -0700 Subject: [PATCH 24/39] trace --- Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium index 400921c17..2bd4af8f9 100755 --- a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium +++ b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium @@ -100,7 +100,7 @@ pipeline { steps{ sh """ . venv/bin/activate - export DJANGO_SETTINGS_MODULE=hhs_oauth_server.settings.logging_it && python manage.py migrate && python manage.py create_admin_groups && python manage.py loaddata scopes.json && python manage.py create_blue_button_scopes && python manage.py create_test_user_and_application && python manage.py create_user_identification_label_selection && python manage.py create_test_feature_switches && (if [ ! -d 'bluebutton-css' ] ; then git clone https://github.com/CMSgov/bluebutton-css.git ; else echo 'CSS already installed.' ; fi) && echo 'starting bb2...' && (python manage.py runserver 0.0.0.0:8000 &) + python manage.py migrate && python manage.py create_admin_groups && python manage.py loaddata scopes.json && python manage.py create_blue_button_scopes && python manage.py create_test_user_and_application && python manage.py create_user_identification_label_selection && python manage.py create_test_feature_switches && (if [ ! -d 'bluebutton-css' ] ; then git clone https://github.com/CMSgov/bluebutton-css.git ; else echo 'CSS already installed.' ; fi) && echo 'starting bb2...' && (export DJANGO_SETTINGS_MODULE=hhs_oauth_server.settings.logging_it && python manage.py runserver 0.0.0.0:8000 &) """ } } From b726c24cb2d84b9ff58e9aae43a49ff7d3d3d282 Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Sat, 4 Sep 2021 13:49:10 -0700 Subject: [PATCH 25/39] trace --- Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium | 1 + 1 file changed, 1 insertion(+) diff --git a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium index 2bd4af8f9..5966855d1 100755 --- a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium +++ b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium @@ -133,6 +133,7 @@ pipeline { steps{ sh """ . venv/bin/activate + ls -al . python runtests.py --selenium apps.integration_tests.selenium_tests.SeleniumTests """ } From 2e1877de23883a5cd26112a054577d67d425e689 Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Sat, 4 Sep 2021 14:23:57 -0700 Subject: [PATCH 26/39] trace --- Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium index 5966855d1..c4a7bdce9 100755 --- a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium +++ b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium @@ -133,7 +133,8 @@ pipeline { steps{ sh """ . venv/bin/activate - ls -al . + ls -al docker-compose/tmp + cat docker-compose/tmp/bb2_logging_test.log python runtests.py --selenium apps.integration_tests.selenium_tests.SeleniumTests """ } From 8d96d3802b20eab36cdf690c216efbf51ccc05b5 Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Sat, 4 Sep 2021 14:59:22 -0700 Subject: [PATCH 27/39] trace --- Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium | 1 + 1 file changed, 1 insertion(+) diff --git a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium index c4a7bdce9..4a9d2e2cd 100755 --- a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium +++ b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium @@ -136,6 +136,7 @@ pipeline { ls -al docker-compose/tmp cat docker-compose/tmp/bb2_logging_test.log python runtests.py --selenium apps.integration_tests.selenium_tests.SeleniumTests + cat docker-compose/tmp/bb2_logging_test.log """ } } From d4e661ca6e15707f4b00a963682942dc420adcdf Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Sat, 4 Sep 2021 17:54:26 -0700 Subject: [PATCH 28/39] trace --- ...insfile.cbc-run-multi-pr-checks-w-selenium | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium index 4a9d2e2cd..e4d02d4ad 100755 --- a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium +++ b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium @@ -105,17 +105,17 @@ pipeline { } } - // stage("RUN logging integration tests") { - // when { - // expression { params.RUN_SELENIUM_TESTS == true } - // } - // steps{ - // sh """ - // . venv/bin/activate - // python runtests.py --selenium apps.integration_tests.logging_tests.LoggingTests.test_auth_fhir_flows_logging - // """ - // } - // } + stage("RUN logging integration tests") { + when { + expression { params.RUN_SELENIUM_TESTS == true } + } + steps{ + sh """ + . venv/bin/activate + python runtests.py --selenium apps.integration_tests.logging_tests.LoggingTests.test_auth_fhir_flows_logging + """ + } + } stage("RUN integration tests") { steps{ @@ -133,10 +133,7 @@ pipeline { steps{ sh """ . venv/bin/activate - ls -al docker-compose/tmp - cat docker-compose/tmp/bb2_logging_test.log python runtests.py --selenium apps.integration_tests.selenium_tests.SeleniumTests - cat docker-compose/tmp/bb2_logging_test.log """ } } From dfa79ddf2d2d85eeb4c918ec29dc22e73708739b Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Sat, 4 Sep 2021 19:51:58 -0700 Subject: [PATCH 29/39] trace --- Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium | 1 + apps/integration_tests/logging_tests.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium index e4d02d4ad..dbaece830 100755 --- a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium +++ b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium @@ -113,6 +113,7 @@ pipeline { sh """ . venv/bin/activate python runtests.py --selenium apps.integration_tests.logging_tests.LoggingTests.test_auth_fhir_flows_logging + cat docker-compose/tmp/bb2_logging_test.log """ } } diff --git a/apps/integration_tests/logging_tests.py b/apps/integration_tests/logging_tests.py index 1da54aad9..caaaed55d 100755 --- a/apps/integration_tests/logging_tests.py +++ b/apps/integration_tests/logging_tests.py @@ -192,7 +192,7 @@ def _validate_events(self): pass # all log events present and validated - self.assertEqual(len(expected_events), 0) + # self.assertEqual(len(expected_events), 0) def test_auth_fhir_flows_logging(self): self.test_auth_grant_fhir_calls_v1() From 8d69bb34b544edf69d02d5aab184014cc2a4357b Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Sat, 4 Sep 2021 20:41:54 -0700 Subject: [PATCH 30/39] turn off pretty json in logging. --- apps/integration_tests/logging_tests.py | 2 +- hhs_oauth_server/settings/logging_it.py | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/apps/integration_tests/logging_tests.py b/apps/integration_tests/logging_tests.py index caaaed55d..1da54aad9 100755 --- a/apps/integration_tests/logging_tests.py +++ b/apps/integration_tests/logging_tests.py @@ -192,7 +192,7 @@ def _validate_events(self): pass # all log events present and validated - # self.assertEqual(len(expected_events), 0) + self.assertEqual(len(expected_events), 0) def test_auth_fhir_flows_logging(self): self.test_auth_grant_fhir_calls_v1() diff --git a/hhs_oauth_server/settings/logging_it.py b/hhs_oauth_server/settings/logging_it.py index 77549ed33..12c813a5c 100755 --- a/hhs_oauth_server/settings/logging_it.py +++ b/hhs_oauth_server/settings/logging_it.py @@ -31,10 +31,6 @@ if logging_logger_audit_handlers is None: raise ValueError("Bad settings, expecting handlers defined in settings.LOGGING for 'audit' logger") - print("------------ append file handler------------") logging_logger_audit_handlers.append('file') - print("------------ LOGGING begin ------------") - print("{}".format(LOGGING)) - print("------------ LOGGING end ------------") -else: - print("------------ no loggers found ------------") + +LOG_JSON_FORMAT_PRETTY = False From 91651219a99fce57fe2fd869ade8f37434332eaa Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Thu, 16 Sep 2021 19:44:05 -0700 Subject: [PATCH 31/39] sync up with Pillow ver bump. --- Dockerfile.selenium | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.selenium b/Dockerfile.selenium index ad68b60cb..7b35180ac 100755 --- a/Dockerfile.selenium +++ b/Dockerfile.selenium @@ -4,7 +4,7 @@ ENV PYTHONUNBUFFERED 1 USER root RUN apt-get update && apt-get install -yq python3.7 python3-pip git RUN pip3 install --upgrade pip -RUN pip3 install selenium psycopg2-binary==2.8.6 pyyaml==5.4.1 Pillow==8.3.1 +RUN pip3 install selenium psycopg2-binary==2.8.6 pyyaml==5.4.1 Pillow==8.3.2 RUN mkdir /code ADD . /code/ WORKDIR /code From 5e5fa70f3af7d4ab25befb1f85230523179cbc7e Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Tue, 5 Oct 2021 16:10:51 -0700 Subject: [PATCH 32/39] print schema to facilitate debugging. --- apps/integration_tests/logging_tests.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/integration_tests/logging_tests.py b/apps/integration_tests/logging_tests.py index 1da54aad9..a89668667 100755 --- a/apps/integration_tests/logging_tests.py +++ b/apps/integration_tests/logging_tests.py @@ -186,6 +186,7 @@ def _validate_events(self): self.assertTrue(re.match(event_desc.get('path_regex'), p)) else: self.assertEqual(p, event_desc.get('path')) + print("SCHEMA={}".format(event_desc.get('schema'))) self.assertTrue(validate_json_schema(event_desc.get('schema'), event_json)) except JSONDecodeError: # skip non json line From b16dd8e2458811d239b0de8bdedbf4eaa7bc15ca Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Tue, 5 Oct 2021 16:22:17 -0700 Subject: [PATCH 33/39] print schema to facilitate debugging. --- apps/integration_tests/logging_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/integration_tests/logging_tests.py b/apps/integration_tests/logging_tests.py index a89668667..04665e847 100755 --- a/apps/integration_tests/logging_tests.py +++ b/apps/integration_tests/logging_tests.py @@ -182,11 +182,11 @@ def _validate_events(self): start_validation = True else: event_desc = expected_events.pop(0) + print("SCHEMA={}".format(event_desc.get('schema'))) if event_desc.get('path_regex') is not None: self.assertTrue(re.match(event_desc.get('path_regex'), p)) else: self.assertEqual(p, event_desc.get('path')) - print("SCHEMA={}".format(event_desc.get('schema'))) self.assertTrue(validate_json_schema(event_desc.get('schema'), event_json)) except JSONDecodeError: # skip non json line From a1607cf0a79519669bbf2de568f60d5e88d234f5 Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Wed, 6 Oct 2021 07:55:25 -0700 Subject: [PATCH 34/39] relax attr check for crosswalk type, for runing under local(mslsx) vs remote(slsx) . --- Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium | 1 - apps/integration_tests/log_event_schemas.py | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium index 7435946f3..38d18aecf 100755 --- a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium +++ b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium @@ -99,7 +99,6 @@ pipeline { sh """ . venv/bin/activate python runtests.py --selenium apps.integration_tests.logging_tests.LoggingTests.test_auth_fhir_flows_logging - cat docker-compose/tmp/bb2_logging_test.log """ } } diff --git a/apps/integration_tests/log_event_schemas.py b/apps/integration_tests/log_event_schemas.py index 2ab982155..b40359730 100755 --- a/apps/integration_tests/log_event_schemas.py +++ b/apps/integration_tests/log_event_schemas.py @@ -136,7 +136,7 @@ "auth_client_id": {"type": "string"}, "auth_app_id": {"type": "string"}, "auth_app_name": {"pattern": "TestApp"}, - "auth_crosswalk_action": {"pattern": "R"}, + "auth_crosswalk_action": {"enum": ["R", "C"]}, "auth_require_demographic_scopes": {"pattern": "^True$"}, "req_user_id": {"type": "number"}, "req_user_username": {"pattern": "fred"}, @@ -169,7 +169,7 @@ "auth_client_id": {"type": "string"}, "auth_app_id": {"type": "string"}, "auth_app_name": {"pattern": "TestApp"}, - "auth_crosswalk_action": {"pattern": "R"}, + "auth_crosswalk_action": {"enum": ["R", "C"]}, "auth_require_demographic_scopes": {"pattern": "^True$"}, "req_qparam_client_id": {"type": "string"}, "req_qparam_response_type": {"pattern": "code"}, @@ -204,7 +204,7 @@ "auth_client_id": {"type": "string"}, "auth_app_id": {"type": "string"}, "auth_app_name": {"pattern": "TestApp"}, - "auth_crosswalk_action": {"pattern": "R"}, + "auth_crosswalk_action": {"enum": ["R", "C"]}, "auth_require_demographic_scopes": {"pattern": "^True$"}, "req_redirect_uri": {"type": "string"}, "req_scope": {"type": "string"}, From 1b14c71379ad253fd1492d52c988c0f7c3d7d777 Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Wed, 6 Oct 2021 08:52:54 -0700 Subject: [PATCH 35/39] cleanup and fix schema checking. --- apps/integration_tests/log_event_schemas.py | 20 ++++++++++---------- apps/integration_tests/logging_tests.py | 1 - 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/apps/integration_tests/log_event_schemas.py b/apps/integration_tests/log_event_schemas.py index b40359730..1d900becb 100755 --- a/apps/integration_tests/log_event_schemas.py +++ b/apps/integration_tests/log_event_schemas.py @@ -139,7 +139,7 @@ "auth_crosswalk_action": {"enum": ["R", "C"]}, "auth_require_demographic_scopes": {"pattern": "^True$"}, "req_user_id": {"type": "number"}, - "req_user_username": {"pattern": "fred"}, + "req_user_username": {"type": "string"}, "req_fhir_id": {"type": "string"}, "path": {"pattern": "/mymedicare/sls-callback"}, "user": {"type": "string"}, @@ -211,7 +211,7 @@ "req_share_demographic_scopes": {"pattern": "^True$"}, "req_allow": {"pattern": "Allow"}, "req_user_id": {"type": "integer"}, - "req_user_username": {"pattern": "fred"}, + "req_user_username": {"type": "string"}, "req_fhir_id": {"type": "string"}, "req_qparam_client_id": {"type": "string"}, "req_qparam_response_type": {"pattern": "code"}, @@ -308,7 +308,7 @@ "request_scheme": {"pattern": "http"}, "response_code": {"type": "integer", "enum": [status.HTTP_200_OK]}, "req_user_id": {"type": "integer"}, - "req_user_username": {"pattern": "fred"}, + "req_user_username": {"type": "string"}, "req_fhir_id": {"type": "string"}, "req_qparam_format": {"pattern": "json"}, "req_qparam_patient": {"type": "string"}, @@ -320,7 +320,7 @@ "access_token_id": {"type": "number"}, "app_require_demographic_scopes": {"type": "boolean"}, "user_id": {"type": "integer"}, - "user_username": {"pattern": "fred"}, + "user_username": {"type": "string"}, "fhir_bundle_type": {"pattern": "searchset|null"}, "fhir_resource_id": {"type": "string"}, "fhir_resource_type": {"pattern": "Bundle|Patient|Coverage|ExplanationOfBenefit"}, @@ -350,7 +350,7 @@ "request_scheme": {"pattern": "http"}, "response_code": {"type": "integer", "enum": [status.HTTP_200_OK]}, "req_user_id": {"type": "integer"}, - "req_user_username": {"pattern": "fred"}, + "req_user_username": {"type": "string"}, "req_fhir_id": {"type": "string"}, "req_qparam__count": {"type": "string"}, "req_qparam_format": {"pattern": "json"}, @@ -364,7 +364,7 @@ "access_token_id": {"type": "number"}, "app_require_demographic_scopes": {"type": "boolean"}, "user_id": {"type": "integer"}, - "user_username": {"pattern": "fred"}, + "user_username": {"type": "string"}, "fhir_bundle_type": {"pattern": "searchset|null"}, "fhir_resource_id": {"type": "string"}, "fhir_resource_type": {"pattern": "Bundle|Patient|Coverage|ExplanationOfBenefit"}, @@ -395,7 +395,7 @@ "request_scheme": {"pattern": "http"}, "response_code": {"type": "integer", "enum": [status.HTTP_200_OK]}, "req_user_id": {"type": "integer"}, - "req_user_username": {"pattern": "fred"}, + "req_user_username": {"type": "string"}, "req_fhir_id": {"type": "string"}, "path": {"pattern": "/v1/fhir/.+"}, "user": {"type": "string"}, @@ -404,7 +404,7 @@ "access_token_id": {"type": "number"}, "app_require_demographic_scopes": {"type": "boolean"}, "user_id": {"type": "integer"}, - "user_username": {"pattern": "fred"}, + "user_username": {"type": "string"}, "fhir_bundle_type": {"pattern": "searchset|null"}, "fhir_resource_id": {"type": "string"}, "fhir_resource_type": {"pattern": "Bundle|Patient|Coverage|ExplanationOfBenefit"}, @@ -434,7 +434,7 @@ "request_scheme": {"pattern": "http"}, "response_code": {"type": "integer", "enum": [status.HTTP_200_OK]}, "req_user_id": {"type": "integer"}, - "req_user_username": {"pattern": "fred"}, + "req_user_username": {"type": "string"}, "req_fhir_id": {"type": "string"}, "path": {"pattern": "/v1/connect/userinfo"}, "user": {"type": "string"}, @@ -443,7 +443,7 @@ "access_token_id": {"type": "number"}, "app_require_demographic_scopes": {"type": "boolean"}, "user_id": {"type": "integer"}, - "user_username": {"pattern": "fred"}, + "user_username": {"type": "string"}, }, "required": ["type", "size", "start_time", "end_time", "ip_addr", "request_uuid", "request_method", "request_scheme", "response_code", diff --git a/apps/integration_tests/logging_tests.py b/apps/integration_tests/logging_tests.py index 04665e847..1da54aad9 100755 --- a/apps/integration_tests/logging_tests.py +++ b/apps/integration_tests/logging_tests.py @@ -182,7 +182,6 @@ def _validate_events(self): start_validation = True else: event_desc = expected_events.pop(0) - print("SCHEMA={}".format(event_desc.get('schema'))) if event_desc.get('path_regex') is not None: self.assertTrue(re.match(event_desc.get('path_regex'), p)) else: From 3c95cd94804f51368fb8edd02ec714ec4efb9039 Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Wed, 6 Oct 2021 09:48:47 -0700 Subject: [PATCH 36/39] cleanup and fix path validation. --- apps/integration_tests/logging_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/integration_tests/logging_tests.py b/apps/integration_tests/logging_tests.py index 1da54aad9..620012865 100755 --- a/apps/integration_tests/logging_tests.py +++ b/apps/integration_tests/logging_tests.py @@ -80,7 +80,7 @@ }, { "schema": LOG_MIDDLEWARE_FHIR_READ_EVENT_SCHEMA, - "path": "/v1/fhir/Patient/-20140000008325" + "path_regex": "/v1/fhir/Patient/-20140000008325|/v1/fhir/Patient/-19990000000001" }, { "schema": LOG_MIDDLEWARE_TESTCLIENT_FHIR_READ_EVENT_SCHEMA, From f045a7b905abdd39b23b81f9b843d0bdefe540be Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Fri, 8 Oct 2021 08:51:31 -0700 Subject: [PATCH 37/39] changes per review, remove django mslsx. --- dev-local/manage.py | 21 --- dev-local/mslsx_django/__init__.py | 0 dev-local/mslsx_django/settings.py | 121 --------------- dev-local/mslsx_django/templates/login.html | 75 --------- dev-local/mslsx_django/urls.py | 20 --- dev-local/mslsx_django/views.py | 163 -------------------- dev-local/mslsx_django/wsgi.py | 16 -- hhs_oauth_server/settings/logging_it.py | 2 +- 8 files changed, 1 insertion(+), 417 deletions(-) delete mode 100755 dev-local/manage.py delete mode 100755 dev-local/mslsx_django/__init__.py delete mode 100755 dev-local/mslsx_django/settings.py delete mode 100755 dev-local/mslsx_django/templates/login.html delete mode 100755 dev-local/mslsx_django/urls.py delete mode 100755 dev-local/mslsx_django/views.py delete mode 100755 dev-local/mslsx_django/wsgi.py diff --git a/dev-local/manage.py b/dev-local/manage.py deleted file mode 100755 index bca5b1fb7..000000000 --- a/dev-local/manage.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python -"""Django's command-line utility for administrative tasks.""" -import os -import sys - - -def main(): - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mslsx_django.settings') - try: - from django.core.management import execute_from_command_line - except ImportError as exc: - raise ImportError( - "Couldn't import Django. Are you sure it's installed and " - "available on your PYTHONPATH environment variable? Did you " - "forget to activate a virtual environment?" - ) from exc - execute_from_command_line(sys.argv) - - -if __name__ == '__main__': - main() diff --git a/dev-local/mslsx_django/__init__.py b/dev-local/mslsx_django/__init__.py deleted file mode 100755 index e69de29bb..000000000 diff --git a/dev-local/mslsx_django/settings.py b/dev-local/mslsx_django/settings.py deleted file mode 100755 index 1e779961f..000000000 --- a/dev-local/mslsx_django/settings.py +++ /dev/null @@ -1,121 +0,0 @@ -""" -Django settings for mslsx_django project. - -Generated by 'django-admin startproject' using Django 2.2.13. - -For more information on this file, see -https://docs.djangoproject.com/en/2.2/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/2.2/ref/settings/ -""" - -import os - -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) -BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = '9iq6l*3n7-ljq#wc-fz496#2ri*zdt2skq!4f$!lx@dt8-!t8=' - -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True - -ALLOWED_HOSTS = ["msls", "localhost", "127.0.0.1"] - - -# Application definition - -INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'mslsx_django', -] - -MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', -] - -ROOT_URLCONF = 'mslsx_django.urls' - -TEMPLATES = [ - { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - ], - }, - }, -] - -WSGI_APPLICATION = 'mslsx_django.wsgi.application' - - -# Database -# https://docs.djangoproject.com/en/2.2/ref/settings/#databases - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), - } -} - - -# Password validation -# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators - -AUTH_PASSWORD_VALIDATORS = [ - { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', - }, -] - - -# Internationalization -# https://docs.djangoproject.com/en/2.2/topics/i18n/ - -LANGUAGE_CODE = 'en-us' - -TIME_ZONE = 'UTC' - -USE_I18N = True - -USE_L10N = True - -USE_TZ = True - - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/2.2/howto/static-files/ - -STATIC_URL = '/static/' diff --git a/dev-local/mslsx_django/templates/login.html b/dev-local/mslsx_django/templates/login.html deleted file mode 100755 index 842f3199b..000000000 --- a/dev-local/mslsx_django/templates/login.html +++ /dev/null @@ -1,75 +0,0 @@ - - - MSLSX Login - - - - -

MSLSX Component Inputs for simulating SLSX/MyMedicare auth flow locally

-

- BB2 Local Dev Warning -

Simulated Authentication no PHI or PII

-

Use Only Synthetic Beneficiary Info

-

Enter values to be returned by the SLSX user_info endpoint below:

-

-
-
-
- {% csrf_token %} -
-

Enter User Name:

- -
-

Enter Identity:

- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- - - -
-
- - diff --git a/dev-local/mslsx_django/urls.py b/dev-local/mslsx_django/urls.py deleted file mode 100755 index bd28a9d12..000000000 --- a/dev-local/mslsx_django/urls.py +++ /dev/null @@ -1,20 +0,0 @@ -from django.conf.urls import url - -from .views import ( - login_page, - login, - token, - userinfo, - signout, - health, -) - -urlpatterns = [ - # mslsx end points - url(r'^sso/authorize\\?.+', login_page), - url(r'^login/', login), - url(r'^health', health), - url(r'^sso/session', token), - url(r'^v1/users/', userinfo), - url(r'^sso/signout', signout), -] diff --git a/dev-local/mslsx_django/views.py b/dev-local/mslsx_django/views.py deleted file mode 100755 index c59745cd3..000000000 --- a/dev-local/mslsx_django/views.py +++ /dev/null @@ -1,163 +0,0 @@ -import json -import base64 - -from django.shortcuts import render, HttpResponse, redirect -from django.views.decorators.csrf import csrf_exempt -from urllib.parse import urlencode - - -signed_in = True - -ENCODE_NAME = "ascii" - -ID_FIELD = "id" -USERNAME_FIELD = "username" -NAME_FIELD = "name" -EMAIL_FIELD = "email" -FIRST_NAME_FIELD = "fisrt_name" -LAST_NAME_FIELD = "last_name" -HICN_FIELD = "hicn" -MBI_FIELD = "mbi" -CODE_KEY = "code" -AUTH_HEADER = "Authorization" - - -def _base64_encode(string): - """ - Removes any `=` used as padding from the encoded string. - """ - encoded = base64.urlsafe_b64encode(string) - return encoded.rstrip(b"=") - - -def _base64_decode(string): - """ - Adds back in the required padding before decoding. - """ - padding = 4 - (len(string) % 4) - string = string + ("=" * padding) - return base64.urlsafe_b64decode(string) - - -def _encode(usr="", name="", first_name="", last_name="", email="", hicn="", mbi=""): - return "{}.{}.{}.{}.{}.{}.{}".format(_base64_encode(usr.encode(ENCODE_NAME)).decode(ENCODE_NAME), - _base64_encode(name.encode(ENCODE_NAME)).decode(ENCODE_NAME), - _base64_encode(first_name.encode(ENCODE_NAME)).decode(ENCODE_NAME), - _base64_encode(last_name.encode(ENCODE_NAME)).decode(ENCODE_NAME), - _base64_encode(email.encode(ENCODE_NAME)).decode(ENCODE_NAME), - _base64_encode(hicn.encode(ENCODE_NAME)).decode(ENCODE_NAME), - _base64_encode(mbi.encode(ENCODE_NAME)).decode(ENCODE_NAME)) - - -def _decode(b64code): - flds = b64code.split(".") - return { - "usr": _base64_decode(flds[0]).decode(ENCODE_NAME), - "name": _base64_decode(flds[1]).decode(ENCODE_NAME), - "first_name": _base64_decode(flds[2]).decode(ENCODE_NAME), - "last_name": _base64_decode(flds[3]).decode(ENCODE_NAME), - "email": _base64_decode(flds[4]).decode(ENCODE_NAME), - "hicn": _base64_decode(flds[5]).decode(ENCODE_NAME), - "mbi": _base64_decode(flds[6]).decode(ENCODE_NAME), - } - - -def login_page(request): - ''' - response with login form - ''' - return render(request, 'login.html', { - "relay": request.GET.get("relay", "missing"), - "redirect_uri": request.GET.get("redirect_uri", "missing"), - }) - - -def login(request): - ''' - process login form POST, collect sub, mbi, hicn, etc. - ''' - - redirect_url = request.POST.get("redirect_uri", None) - - req_token = _encode(request.POST.get(USERNAME_FIELD, ""), - request.POST.get(NAME_FIELD, ""), - request.POST.get(FIRST_NAME_FIELD, ""), - request.POST.get(LAST_NAME_FIELD, ""), - request.POST.get(EMAIL_FIELD, ""), - request.POST.get(HICN_FIELD, ""), - request.POST.get(MBI_FIELD, "")) - - qparams = { - "req_token": req_token, - "relay": request.POST.get("relay", "") - } - return redirect("{}?{}".format(redirect_url, urlencode(qparams))) - - -def health(request): - ''' - always good - ''' - return HttpResponse(json.dumps({"message": "all's well"}), - status=200, content_type='application/json') - - -@csrf_exempt -def token(request): - ''' - grant access token to further get userinfo - ''' - body_unicode = request.body.decode('utf-8') - body = json.loads(body_unicode) - request_token = body.get("request_token", None) - - if request_token is None: - return HttpResponse(json.dumps({"message": "Bad Request, missing request token."}), - status=400, content_type='application/json') - - user_info = _decode(request_token) - - token = { - "user_id": user_info['usr'], - "auth_token": request_token, - } - - return HttpResponse(json.dumps(token), status=200, content_type='application/json') - - -def userinfo(request): - - global signed_in - - if signed_in is True: - tkn = request.headers.get(AUTH_HEADER, None) - if tkn is None: - return HttpResponse(json.dumps({"message": "Bad Request, missing request token."}), - status=400, content_type='application/json') - if not tkn.startswith("Bearer "): - return HttpResponse(json.dumps({"message": "Bad Request, malformed bearer token."}), - status=400, content_type='application/json') - tkn = tkn.split()[1] - user_info = _decode(tkn) - slsx_userinfo = { - "data": { - "user": { - "id": user_info["usr"], - "username": user_info["name"], - "email": user_info["email"], - "firstName": user_info["first_name"], - "lastName": user_info["last_name"], - "hicn": user_info["hicn"], - "mbi": user_info["mbi"], - }, - }, - } - signed_in = False - return HttpResponse(json.dumps(slsx_userinfo), status=200, content_type='application/json') - else: - signed_in = True - return HttpResponse(status=403, content_type='application/json') - - -def signout(request): - return HttpResponse(status=302, content_type='application/json') diff --git a/dev-local/mslsx_django/wsgi.py b/dev-local/mslsx_django/wsgi.py deleted file mode 100755 index dea691653..000000000 --- a/dev-local/mslsx_django/wsgi.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -WSGI config for mslsx_django project. - -It exposes the WSGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/ -""" - -import os - -from django.core.wsgi import get_wsgi_application - -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mslsx_django.settings') - -application = get_wsgi_application() diff --git a/hhs_oauth_server/settings/logging_it.py b/hhs_oauth_server/settings/logging_it.py index 12c813a5c..a1f026187 100755 --- a/hhs_oauth_server/settings/logging_it.py +++ b/hhs_oauth_server/settings/logging_it.py @@ -1,4 +1,4 @@ -from .dev import * +from .dev import * # lgtm [py/polluting-import] # Override audit logging handler with a file handler logging_handlers = LOGGING['handlers'] From 64c1503d1bbf8406399bc5705c972a9b14d26827 Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Fri, 8 Oct 2021 09:35:28 -0700 Subject: [PATCH 38/39] catch up fix of docker-compose.selenium.yaml revert back to go lang mslsx. --- docker-compose.selenium.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docker-compose.selenium.yml b/docker-compose.selenium.yml index 9861107b6..58675977e 100755 --- a/docker-compose.selenium.yml +++ b/docker-compose.selenium.yml @@ -38,14 +38,13 @@ services: - "5900:5900" msls: - build: . - command: bash -c "cd dev-local ; python manage.py migrate ; python3 -m debugpy --listen 0.0.0.0:7890 manage.py runserver 0.0.0.0:8080 --noreload" - environment: - - DJANGO_SETTINGS_MODULE=mslsx_django.settings + build: + context: ./dev-local/msls + dockerfile: Dockerfile + command: msls ports: - - "8080:8080" - - "7890:7890" - + - "8080:8080" + db: image: postgres environment: From fab58bed98c60efa9fcd37271958d956f2160532 Mon Sep 17 00:00:00 2001 From: Dave Tisza Date: Tue, 12 Oct 2021 15:59:22 -0400 Subject: [PATCH 39/39] Fix local dev docker-compose issues - Add healthcheck to postgres startup and bb2slsx to depend on service_healthy to fix timing issue in docker-compose.selenium.yml - Move DJANGO_SETTINGS_MODULE location in launcher script docker-compose/run_selenium_tests_local_keybase.sh to enable other types of tests (non logit) to run. BB2-537 --- docker-compose.selenium.yml | 13 +++++++++++-- docker-compose/run_selenium_tests_local_keybase.sh | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/docker-compose.selenium.yml b/docker-compose.selenium.yml index 58675977e..75516f19b 100755 --- a/docker-compose.selenium.yml +++ b/docker-compose.selenium.yml @@ -52,6 +52,12 @@ services: - POSTGRES_PASSWORD=toor ports: - "5432:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 10s + timeout: 5s + retries: 5 + bb2slsx: build: . @@ -84,5 +90,8 @@ services: - "8000:8000" - "5678:5678" depends_on: - - db - - msls + msls: + condition: service_started + db: + condition: service_healthy + diff --git a/docker-compose/run_selenium_tests_local_keybase.sh b/docker-compose/run_selenium_tests_local_keybase.sh index 542f21add..4d58df17b 100755 --- a/docker-compose/run_selenium_tests_local_keybase.sh +++ b/docker-compose/run_selenium_tests_local_keybase.sh @@ -28,6 +28,7 @@ FHIR_URL="https://prod-sbx.bfd.cms.gov" # List of tests to run. To be passed in to runtests.py. TESTS_LIST="apps.integration_tests.selenium_tests.SeleniumTests" +export DJANGO_SETTINGS_MODULE="hhs_oauth_server.settings.logging_it" # Echo function that includes script name on each line for console log readability echo_msg () { @@ -96,7 +97,6 @@ else if [[ $1 == "logit" ]] then TESTS_LIST="apps.integration_tests.logging_tests.LoggingTests.test_auth_fhir_flows_logging" - export DJANGO_SETTINGS_MODULE="hhs_oauth_server.settings.logging_it" export DJANGO_LOG_JSON_FORMAT_PRETTY=False fi fi