diff --git a/Dockerfile b/Dockerfile index af9338dff..070be277b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,8 +10,5 @@ RUN unzip /tmp/sonar-scanner-cli-5.0.1.3006-linux.zip -d /tmp RUN python -m venv /tmp/venv RUN . /tmp/venv/bin/activate ENV PATH="/tmp/sonar-scanner-5.0.1.3006-linux/bin:/tmp/venv/bin:${PATH}" -RUN pip install --upgrade pip -RUN pip install --upgrade pip-tools -RUN pip install --upgrade setuptools -RUN pip install --upgrade coverage -RUN pip install -r requirements/requirements.dev.txt --no-index --find-links ./vendor/ \ No newline at end of file +RUN pip install --upgrade pip pip-tools setuptools coverage +RUN pip install -r requirements/requirements.dev.txt --no-index --find-links ./vendor/ diff --git a/Dockerfile.selenium b/Dockerfile.selenium index 0acfde1e8..c2757e9de 100755 --- a/Dockerfile.selenium +++ b/Dockerfile.selenium @@ -1,16 +1,18 @@ -FROM seleniarm/standalone-chromium +FROM selenium/standalone-chromium -ENV PYTHONUNBUFFERED 1 USER root -#RUN apt-get update ; apt-get install -yq git curl libpq-dev libffi-dev + RUN apt-get update ; apt-get install -yq python3 python3-venv RUN ln -s /usr/bin/python3 /usr/local/bin/python -RUN useradd -m -s /bin/bash DEV -USER DEV + +# switch to existing seluser from selenium docker +USER seluser + ADD . /code WORKDIR /code RUN python -m venv /tmp/venv RUN . /tmp/venv/bin/activate ENV PATH="/tmp/venv/bin:${PATH}" + RUN pip3 install --upgrade pip RUN pip3 install selenium pytest debugpy jsonschema python-dateutil diff --git a/Dockerfiles/Dockerfile.selenium-jenkins b/Dockerfiles/Dockerfile.selenium-jenkins deleted file mode 100755 index 7280a63af..000000000 --- a/Dockerfiles/Dockerfile.selenium-jenkins +++ /dev/null @@ -1,22 +0,0 @@ -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 pip install --upgrade pip - -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/Dockerfile.selenium-jenkins-python311 b/Dockerfiles/Dockerfile.selenium-jenkins-python311 deleted file mode 100755 index 9f31ac753..000000000 --- a/Dockerfiles/Dockerfile.selenium-jenkins-python311 +++ /dev/null @@ -1,18 +0,0 @@ -FROM --platform=linux/amd64 python:3.11 -# For build CBC Jenkins job ECR image -ENV PYTHONUNBUFFERED 1 - -RUN mkdir /code -ADD . /code/ -WORKDIR /code - -RUN pip install --upgrade pip -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/Dockerfile.selenium-jenkins-python311-plus-chromedriver b/Dockerfiles/Dockerfile.selenium-jenkins-python311-plus-chromedriver new file mode 100644 index 000000000..4edc89e03 --- /dev/null +++ b/Dockerfiles/Dockerfile.selenium-jenkins-python311-plus-chromedriver @@ -0,0 +1,27 @@ +FROM --platform=linux/amd64 python:3.11 +# For build CBC Jenkins job ECR image +ENV PYTHONUNBUFFERED 1 + +RUN mkdir /code +ADD . /code/ +WORKDIR /code + +RUN pip install --upgrade pip +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: keep the previous chrome driver install code for reference +# 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 + +# hard code the zip URL here since `curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE` still points to 114 which is out of date +# this is the current way google publish the chrome drivers, going forward, need to make changes to keep up with the way google publish the +# drivers. +RUN wget -O /tmp/chromedriver.zip https://storage.googleapis.com/chrome-for-testing-public/131.0.6778.108/linux64/chromedriver-linux64.zip \ + && unzip -p /tmp/chromedriver.zip chromedriver-linux64/chromedriver > /usr/local/bin/chromedriver \ + && chmod +x /usr/local/bin/chromedriver diff --git a/Dockerfiles/Dockerfile.selenium-jenkins-python38 b/Dockerfiles/Dockerfile.selenium-jenkins-python38 deleted file mode 100644 index e0c8dafd5..000000000 --- a/Dockerfiles/Dockerfile.selenium-jenkins-python38 +++ /dev/null @@ -1,18 +0,0 @@ -FROM python:3.8 -# For build CBC Jenkins job ECR image -ENV PYTHONUNBUFFERED 1 - -RUN mkdir /code -ADD . /code/ -WORKDIR /code - -RUN pip install --upgrade pip -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 index 9dd2bf502..5d9395243 100755 --- a/Dockerfiles/readme.md +++ b/Dockerfiles/readme.md @@ -1,13 +1,11 @@ -# Build, Tag, and Publish integration and selenium tests ECR iamge +# Build, Tag, and Publish integration and selenium tests ECR image - used by github CI check 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.selenium-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 +docker build -f Dockerfile.selenium-jenkins-python311-plus-chromedriver -t bb2-cbc-build-selenium-python311-chromium . +docker tag bb2-cbc-build-selenium-python311-chromium:latest public.ecr.aws/f5g8o1y9/bb2-cbc-build-selenium-python311-chromium:latest +docker push public.ecr.aws/f5g8o1y9/bb2-cbc-build-selenium-python311-chromium:latest +``` diff --git a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium index 959d3db68..19697aa11 100755 --- a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium +++ b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium @@ -56,7 +56,6 @@ pipeline { python -m venv venv . venv/bin/activate python -m pip install --upgrade pip setuptools wheel cryptography - make reqs-download make reqs-install-dev pip install --upgrade coverage """ @@ -101,7 +100,7 @@ pipeline { steps{ sh """ . venv/bin/activate - pytest ./apps/integration_tests/logging_tests.py::TestLoggings::test_auth_fhir_flows_logging + USE_NEW_PERM_SCREEN=true pytest ./apps/integration_tests/logging_tests.py::TestLoggings::test_auth_fhir_flows_logging """ } } @@ -136,7 +135,7 @@ pipeline { sh 'echo "RUN selenium tests - testclient based authorization flow tests and data flow tests"' sh """ . venv/bin/activate - pytest ./apps/integration_tests/selenium_tests.py + USE_NEW_PERM_SCREEN=true pytest ./apps/integration_tests/selenium_tests.py """ } } diff --git a/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium-chromium b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium-chromium new file mode 100644 index 000000000..0a7ce0d1b --- /dev/null +++ b/Jenkinsfiles/Jenkinsfile.cbc-run-multi-pr-checks-w-selenium-chromium @@ -0,0 +1,151 @@ +pipeline { + agent { + kubernetes { + defaultContainer "bb2-cbc-build-selenium-python311-chromium" + yamlFile "Jenkinsfiles/cbc-pod-deployment-config-w-selenium-p311-chromium.yaml" + } + } + + environment { + DJANGO_LOG_JSON_FORMAT_PRETTY = true + DJANGO_SETTINGS_MODULE = "hhs_oauth_server.settings.logging_it" + OAUTHLIB_INSECURE_TRANSPORT = true + DJANGO_SECURE_SESSION = false + DJANGO_FHIR_CERTSTORE = "./certstore" + // use mock login - safer, faster + USE_MSLSX = true + 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}" + HOSTNAME_URL = "http://localhost:8000" + } + + 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: false, + 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 """ + pip3 install --upgrade pip setuptools wheel + pip3 install -r requirements/requirements.dev.txt --no-index --find-links ./vendor/ + """ + } + } + + stage("CHECK Flake8 Python Lint/Style") { + steps{ + sh """ + flake8 + """ + } + } + + stage("START BB2 server in background") { + when { + expression { params.RUN_SELENIUM_TESTS == true } + } + steps{ + sh """ + mkdir ./docker-compose/tmp/ + (python3 ./dev-local/app.py&) && + python3 manage.py migrate && + python3 manage.py create_admin_groups && + python3 manage.py loaddata scopes.json && + python3 manage.py create_blue_button_scopes && + python3 manage.py create_test_user_and_application && + python3 manage.py create_user_identification_label_selection && + python3 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 && python3 manage.py runserver 0.0.0.0:8000 > ./docker-compose/tmp/bb2_email_to_stdout.log 2>&1 &) + """ + } + } + + stage("RUN logging integration tests") { + when { + expression { params.RUN_SELENIUM_TESTS == true } + } + steps{ + catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') { + sh """ + USE_NEW_PERM_SCREEN=true ON_REMOTE_CI=true pytest -s ./apps/integration_tests/logging_tests.py::TestLoggings::test_auth_fhir_flows_logging + """ + } + sh """ + echo '======================' + cat ./docker-compose/tmp/bb2_email_to_stdout.log + echo '======================' + """ + } + } + + stage("RUN selenium user and apps management tests") { + when { + expression { params.RUN_SELENIUM_TESTS == true } + } + steps{ + sh 'echo "RUN selenium tests - user account and app management tests"' + sh """ + USE_NEW_PERM_SCREEN=true ON_REMOTE_CI=true pytest -s ./apps/integration_tests/selenium_accounts_tests.py::TestUserAndAppMgmt::testAccountAndAppMgmt + """ + } + } + + stage("RUN integration tests") { + steps{ + sh """ + python3 runtests.py --integration apps.integration_tests.integration_test_fhir_resources.IntegrationTestFhirApiResources + """ + } + } + + stage("RUN Django Unit Tests") { + steps{ + sh """ + python3 runtests.py + """ + } + } + + stage("RUN selenium tests") { + when { + expression { params.RUN_SELENIUM_TESTS == true } + } + steps{ + sh 'echo "RUN selenium tests - testclient based authorization flow tests and data flow tests"' + sh """ + USE_NEW_PERM_SCREEN=true ON_REMOTE_CI=true pytest -s ./apps/integration_tests/selenium_tests.py + """ + } + } + } +} diff --git a/Jenkinsfiles/cbc-pod-deployment-config-w-selenium-p311-chromium.yaml b/Jenkinsfiles/cbc-pod-deployment-config-w-selenium-p311-chromium.yaml new file mode 100644 index 000000000..016438567 --- /dev/null +++ b/Jenkinsfiles/cbc-pod-deployment-config-w-selenium-p311-chromium.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Pod +spec: + containers: + - name: bb2-cbc-build-selenium-python311-chromium + image: "public.ecr.aws/f5g8o1y9/bb2-cbc-build-selenium-python311-chromium:latest" + tty: true + command: ["tail", "-f"] + imagePullPolicy: Always + nodeSelector: + Agents: true diff --git a/README.md b/README.md index 903baa2b2..e8285f23b 100644 --- a/README.md +++ b/README.md @@ -167,4 +167,4 @@ by their respective authors. License ------- -This project is free and open source software under the Apache 2 license. See LICENSE for more information. \ No newline at end of file +This project is free and open source software under the Apache 2 license. See LICENSE for more information. diff --git a/apps/authorization/models.py b/apps/authorization/models.py index 91720a387..f3761d176 100644 --- a/apps/authorization/models.py +++ b/apps/authorization/models.py @@ -8,7 +8,6 @@ from django.utils import timezone from oauth2_provider.settings import oauth2_settings from oauth2_provider.models import get_access_token_model -from waffle import get_waffle_flag_model class DataAccessGrant(models.Model): @@ -41,22 +40,17 @@ def user(self): def update_expiration_date(self): # For THIRTEEN_MONTH type update expiration_date - if self.application: - flag = get_waffle_flag_model().get("limit_data_access") - if flag.rollout or (flag.id is not None and flag.is_active_for_user(self.application.user)): - if self.application.data_access_type == "THIRTEEN_MONTH": - self.expiration_date = datetime.now().replace( - tzinfo=pytz.UTC - ) + relativedelta(months=+13) - self.save() + if self.application and self.application.data_access_type == "THIRTEEN_MONTH": + self.expiration_date = datetime.now().replace( + tzinfo=pytz.UTC + ) + relativedelta(months=+13) + self.save() def has_expired(self): - flag = get_waffle_flag_model().get("limit_data_access") - if flag.rollout or (flag.id is not None and flag.is_active_for_user(self.application.user)): - if self.application.data_access_type == "THIRTEEN_MONTH": - if self.expiration_date: - if self.expiration_date < datetime.now().replace(tzinfo=pytz.UTC): - return True + if self.application.data_access_type == "THIRTEEN_MONTH": + if self.expiration_date: + if self.expiration_date < datetime.now().replace(tzinfo=pytz.UTC): + return True return False diff --git a/apps/authorization/tests/test_data_access_grant.py b/apps/authorization/tests/test_data_access_grant.py index b7a83bc1b..6f0a82df2 100644 --- a/apps/authorization/tests/test_data_access_grant.py +++ b/apps/authorization/tests/test_data_access_grant.py @@ -18,15 +18,14 @@ get_access_token_model, ) -from apps.test import BaseApiTest, flag_is_active +from apps.test import BaseApiTest from apps.authorization.models import ( DataAccessGrant, ArchivedDataAccessGrant, check_grants, update_grants, ) -from waffle import get_waffle_flag_model -from waffle.testutils import override_flag, override_switch +from waffle.testutils import override_switch Application = get_application_model() AccessToken = get_access_token_model() @@ -95,56 +94,7 @@ def test_create_update_delete(self): # Verify expiration_date copied OK. self.assertEqual("2030-01-15 00:00:00+00:00", str(arch_dag.expiration_date)) - @override_flag("limit_data_access", active=False) - def test_thirteen_month_app_type_without_switch_limit_data_access(self): - assert not flag_is_active("limit_data_access") - - # 1. Create bene and app for tests - dev_user = self._create_user("developer_test", "123456") - bene_user = self._create_user("test_beneficiary", "123456") - test_app = self._create_application( - "test_app", user=dev_user, data_access_type="THIRTEEN_MONTH" - ) - - flag = get_waffle_flag_model().get("limit_data_access") - assert flag.id is None or flag.is_active_for_user(dev_user) is False - - # 2. Create grant with expiration date in future. - dag = DataAccessGrant.objects.create( - application=test_app, beneficiary=bene_user - ) - - # 3. Test expiration_date not set - self.assertEqual(dag.expiration_date, None) - # Test has_expired() with None is false - self.assertEqual(dag.has_expired(), False) - - # 4. Test has_expired() true for -1 hour ago is false w/o switch enabled - dag.expiration_date = datetime.now().replace(tzinfo=pytz.UTC) + relativedelta( - hours=-1 - ) - self.assertEqual(dag.has_expired(), False) - - # 5. Test has_expired() false for +1 hour in future. - dag.expiration_date = datetime.now().replace(tzinfo=pytz.UTC) + relativedelta( - hours=+1 - ) - self.assertEqual(dag.has_expired(), False) - - # 6. Test has_expired() false for ONE_TIME type - test_app.data_access_type = "ONE_TIME" - test_app.save() - self.assertEqual(dag.has_expired(), False) - - # 7. Test has_expired() false for RESEARCH_STUDY type - test_app.data_access_type = "RESEARCH_STUDY" - test_app.save() - self.assertEqual(dag.has_expired(), False) - - @override_flag("limit_data_access", active=True) def test_thirteen_month_app_type_with_switch_limit_data_access(self): - assert flag_is_active("limit_data_access") - # 1. Create bene and app for tests dev_user = self._create_user("developer_test", "123456") bene_user = self._create_user("test_beneficiary", "123456") @@ -152,9 +102,6 @@ def test_thirteen_month_app_type_with_switch_limit_data_access(self): "test_app", user=dev_user, data_access_type="THIRTEEN_MONTH" ) - flag = get_waffle_flag_model().get("limit_data_access") - assert flag.id is not None and flag.is_active_for_user(dev_user) - # 2. Create grant with expiration date in future. dag = DataAccessGrant.objects.create( application=test_app, beneficiary=bene_user @@ -463,40 +410,3 @@ def test_permission_deny_on_app_or_org_disabled(self): # set back app and user to active - not to affect other tests application.active = True application.save() - - def test_thirteen_month_app_needs_limit_data_access_set(self): - - # 1. Create benes - dev_user = self._create_user("developer_test", "123456") - bene_user = self._create_user("test_beneficiary", "123456") - flag_bene_user = self._create_user("flag_beneficiary", "123456") - test_app = self._create_application( - "test_app", user=dev_user, data_access_type="THIRTEEN_MONTH" - ) - - # 2. Create flag and show is not set for dev_user - flag = get_waffle_flag_model().objects.create(name="limit_data_access") - assert flag.id is not None - assert not flag.is_active_for_user(dev_user) - - # 3. Create grant and expire expiration to show it doesn't matter - dag = DataAccessGrant.objects.create( - application=test_app, beneficiary=bene_user - ) - # 4. Test has_expired() true for -1 hour ago - dag.expiration_date = datetime.now().replace(tzinfo=pytz.UTC) + relativedelta( - hours=-1 - ) - self.assertEqual(dag.has_expired(), False) - - # 4. Add dev_user to flag - flag.users.add(dev_user) - - # 5. Create new grant and show expiration is working - flag_dag = DataAccessGrant.objects.create( - application=test_app, beneficiary=flag_bene_user - ) - flag_dag.expiration_date = datetime.now().replace( - tzinfo=pytz.UTC - ) + relativedelta(hours=-1) - self.assertEqual(dag.has_expired(), True) diff --git a/apps/authorization/tests/test_data_access_grant_permissions.py b/apps/authorization/tests/test_data_access_grant_permissions.py index 657cb959e..d2c326e1f 100644 --- a/apps/authorization/tests/test_data_access_grant_permissions.py +++ b/apps/authorization/tests/test_data_access_grant_permissions.py @@ -7,9 +7,8 @@ from httmock import HTTMock, urlmatch from oauth2_provider.models import get_access_token_model, get_refresh_token_model from unittest import mock -from waffle.testutils import override_flag -from apps.test import BaseApiTest, flag_is_active +from apps.test import BaseApiTest from apps.authorization.models import ( DataAccessGrant, ) @@ -257,18 +256,12 @@ def _assert_call_token_refresh_endpoint( ) return content - @override_flag("limit_data_access", active=False) - def test_revoked_data_access_grant_without_flag_limit_data_access(self): + def test_revoked_data_access_grant(self): """ Test data access grant deleted / revoked - Test data access for FHIR and profile end points - with limit_data_access flag False. - - This will be the flag setting in Sandbox. + Test data access for FHIR and profile end points. """ - assert not flag_is_active("limit_data_access") - # 1. Use helper method to create app, user, authorized grant & access token. user, app, ac = self._create_user_app_token_grant( first_name="first", @@ -277,10 +270,11 @@ def test_revoked_data_access_grant_without_flag_limit_data_access(self): app_name="test_app1", app_username="devuser1", app_user_organization="org1", + app_data_access_type="RESEARCH_STUDY", ) # 2. Test application data access type - self.assertEqual(app.data_access_type, "THIRTEEN_MONTH") + self.assertEqual(app.data_access_type, "RESEARCH_STUDY") # 3. Test grant obj created OK. dag = DataAccessGrant.objects.get(beneficiary=user, application=app) @@ -329,116 +323,12 @@ def test_revoked_data_access_grant_without_flag_limit_data_access(self): expected_response_detail_mesg="Authentication credentials were not provided.", ) - @override_flag("limit_data_access", active=False) - def test_research_study_app_type_without_flag_limit_data_access(self): + def test_research_study_app_type(self): """ Test Application.data_access_type="RESEARCH_STUDY". Test data access for FHIR and profile end points - with limit_data_access flag False. - - This will be the flag setting in Sandbox. """ - assert not flag_is_active("limit_data_access") - - # 1. Use helper method to create app, user, authorized grant & access token. - user, app, ac = self._create_user_app_token_grant( - first_name="first", - last_name="last1", - fhir_id="-20140000008325", - app_name="test_app1", - app_username="devuser1", - app_user_organization="org1", - app_data_access_type="RESEARCH_STUDY", - ) - self.assertEqual(app.data_access_type, "RESEARCH_STUDY") - - # Test grant exists. - self.assertTrue( - DataAccessGrant.objects.filter( - beneficiary=user, - application=app, - ).exists() - ) - - # 2. Test that all calls are successful (response_code=200) - self._assert_call_all_fhir_endpoints( - access_token=ac["access_token"], expected_response_code=200 - ) - - # 3. Test token refresh is successful (response_code=200) - ac = self._assert_call_token_refresh_endpoint( - application=app, - refresh_token=ac["refresh_token"], - expected_response_code=200, - ) - - # 4. Set application to in-active/disabled - app.active = False - app.save() - - # 5. Test FHIR end point while app in-active/disabled (response_code=401) - self._assert_call_all_fhir_endpoints( - access_token=ac["access_token"], - expected_response_code=401, - expected_response_detail_mesg=settings.APPLICATION_TEMPORARILY_INACTIVE.format( - app.name - ), - ) - - # 6. Test token refresh after applciation in-active/disabled (response_code=401) - self._assert_call_token_refresh_endpoint( - application=app, - refresh_token=ac["refresh_token"], - expected_response_code=401, - expected_response_error_mesg="invalid_client", - expected_response_error_description_mesg=settings.APPLICATION_TEMPORARILY_INACTIVE.format( - app.name - ), - ) - - # 7. Set application to active/endabled - app.active = True - app.save() - - self._assert_call_all_fhir_endpoints( - access_token=ac["access_token"], expected_response_code=200 - ) - - # 9. Test app not expired token refresh (response_code=200) - ac = self._assert_call_token_refresh_endpoint( - application=app, - refresh_token=ac["refresh_token"], - expected_response_code=200, - ) - - # 10. Test with RESEARCH_STUDY application end_date IS expired w/o feature flag (response_code=200) - app.data_access_type = "RESEARCH_STUDY" - app.save() - - self._assert_call_all_fhir_endpoints( - access_token=ac["access_token"], expected_response_code=200 - ) - - # 11. Test app expired token refresh (response_code=200) - ac = self._assert_call_token_refresh_endpoint( - application=app, - refresh_token=ac["refresh_token"], - expected_response_code=200, - ) - - @override_flag("limit_data_access", active=True) - def test_research_study_app_type_with_flag_limit_data_access(self): - """ - Test Application.data_access_type="RESEARCH_STUDY". - - Test data access for FHIR and profile end points - with limit_data_access flag True. - - This will be the flag setting in PROD. - """ - assert flag_is_active("limit_data_access") - # 1. Use helper method to create app, user, authorized grant & access token. user, app, ac = self._create_user_app_token_grant( first_name="first", @@ -514,66 +404,12 @@ def test_research_study_app_type_with_flag_limit_data_access(self): expected_response_code=200, ) - @override_flag("limit_data_access", active=False) - def test_one_time_app_type_without_flag_limit_data_access(self): + def test_one_time_app_type(self): """ Test Application.data_access_type="ONE_TIME" - with limit_data_access flag False. - - This will be the flag setting in Sandbox. NOTE: This type of application does not allow token refreshes - when the feature flag is enabled. """ - assert not flag_is_active("limit_data_access") - - # 1. Use helper method to create app, user, authorized grant & access token. - user, app, ac = self._create_user_app_token_grant( - first_name="first", - last_name="last1", - fhir_id="-20140000008325", - app_name="test_app1", - app_username="devuser1", - app_user_organization="org1", - app_data_access_type="ONE_TIME", - ) - - # Test application default data access type - self.assertEqual(app.data_access_type, "ONE_TIME") - - # Test grant exists. - self.assertTrue( - DataAccessGrant.objects.filter( - beneficiary=user, - application=app, - ).exists() - ) - - # 2. Test token refresh is working OK (response_code=200) - ac = self._assert_call_token_refresh_endpoint( - application=app, - refresh_token=ac["refresh_token"], - expected_response_code=200, - ) - - # 3. Test that all calls are successful (response_code=200) - self._assert_call_all_fhir_endpoints( - access_token=ac["access_token"], expected_response_code=200 - ) - - @override_flag("limit_data_access", active=True) - def test_one_time_app_type_with_flag_limit_data_access(self): - """ - Test Application.data_access_type="ONE_TIME" - with limit_data_access flag True - - This will be the flag setting in PROD. - - NOTE: This type of application does not allow token refreshes - when the feature flag is enabled. - """ - assert flag_is_active("limit_data_access") - # 1. Use helper method to create app, user, authorized grant & access token. user, app, ac = self._create_user_app_token_grant( first_name="first", @@ -610,62 +446,11 @@ def test_one_time_app_type_with_flag_limit_data_access(self): access_token=ac["access_token"], expected_response_code=200 ) - @override_flag("limit_data_access", active=False) @mock.patch("apps.authorization.models.datetime", StubDate) - def test_thirteen_month_app_type_without_flag_limit_data_access(self): + def test_thirteen_month_app_type(self): """ Test Application.data_access_type="THIRTEEN_MONTH" - with limit_data_access flag False - - This will be the flag setting in Sandbox. """ - assert not flag_is_active("limit_data_access") - - # 1. Use helper method to create app, user, authorized grant & access token. - user, app, ac = self._create_user_app_token_grant( - first_name="first", - last_name="last1", - fhir_id="-20140000008325", - app_name="test_app1", - app_username="devuser1", - app_user_organization="org1", - app_data_access_type="THIRTEEN_MONTH", - ) - - # 2. Test application data access type - self.assertEqual(app.data_access_type, "THIRTEEN_MONTH") - - # 3. Test grant obj created OK. - dag = DataAccessGrant.objects.get(beneficiary=user, application=app) - # Assert is not None - self.assertNotEqual(dag, None) - - # Assert expiration date has NOT been set - self.assertEqual(dag.expiration_date, None) - - # 4. Test token refresh is enabled for app (response_code=200) - ac = self._assert_call_token_refresh_endpoint( - application=app, - refresh_token=ac["refresh_token"], - expected_response_code=200, - ) - - # 5. Test that all calls are successful (response_code=200) - self._assert_call_all_fhir_endpoints( - access_token=ac["access_token"], expected_response_code=200 - ) - - @override_flag("limit_data_access", active=True) - @mock.patch("apps.authorization.models.datetime", StubDate) - def test_thirteen_month_app_type_with_flag_limit_data_access(self): - """ - Test Application.data_access_type="THIRTEEN_MONTH" - with limit_data_access flag True - - This will be the flag setting in SBX. - """ - assert flag_is_active("limit_data_access") - # 1. Use helper method to create app, user, authorized grant & access token. user, app, ac = self._create_user_app_token_grant( first_name="first", @@ -780,7 +565,6 @@ def test_thirteen_month_app_type_with_flag_limit_data_access(self): access_token=ac["access_token"], expected_response_code=200 ) - @override_flag("limit_data_access", active=False) def test_data_access_grant_permissions_has_permission(self): """ Test edge case bug fix for BB2-2130 @@ -791,7 +575,6 @@ def test_data_access_grant_permissions_has_permission(self): DataAccessGrant.DoesNotExist: DataAccessGrant matching query does not exist. """ - # 1. Use helper method to create app, user, authorized grant & access token. user, app, ac = self._create_user_app_token_grant( first_name="first", diff --git a/apps/capabilities/permissions.py b/apps/capabilities/permissions.py index a7bc3b726..ece09f048 100644 --- a/apps/capabilities/permissions.py +++ b/apps/capabilities/permissions.py @@ -37,20 +37,15 @@ def has_permission(self, request, view): slug__in=token_scopes ).values_list('protected_resources', flat=True).all()) - # this is a shorterm fix to reject all tokens that do not have either - # patient/coverage.read or patient/ExplanationOfBenefit.read - if ("patient/Coverage.read" in token_scopes) or ("patient/ExplanationOfBenefit.read" in token_scopes): - for scope in scopes: - for method, path in json.loads(scope): - if method != request.method: - continue - if path == request.path: - return True - if re.fullmatch(path, request.path) is not None: - return True - return False - else: - return False + for scope in scopes: + for method, path in json.loads(scope): + if method != request.method: + continue + if path == request.path: + return True + if re.fullmatch(path, request.path) is not None: + return True + return False else: # BB2-237: Replaces ASSERT with exception. We should never reach here. mesg = ("TokenHasScope requires the `oauth2_provider.rest_framework.OAuth2Authentication`" diff --git a/apps/capabilities/tests.py b/apps/capabilities/tests.py index b320398f5..396faf9d2 100644 --- a/apps/capabilities/tests.py +++ b/apps/capabilities/tests.py @@ -1,5 +1,4 @@ import json -import unittest from django.contrib.auth.models import Group from django.test import TestCase @@ -41,7 +40,6 @@ def setUp(self): protected_resources=json.dumps([["POST", "/path"]]), ) - @unittest.skip("Broke with quick fix") def test_request_is_protected(self): request = SimpleRequest("scope") request.method = "GET" diff --git a/apps/core/management/commands/create_test_feature_switches.py b/apps/core/management/commands/create_test_feature_switches.py index 65b45c85b..690ad192a 100644 --- a/apps/core/management/commands/create_test_feature_switches.py +++ b/apps/core/management/commands/create_test_feature_switches.py @@ -17,7 +17,6 @@ ) WAFFLE_FEATURE_FLAGS = ( - ("limit_data_access", ['fred']), ) diff --git a/apps/docs/templates/oauth2-redirect.html b/apps/docs/templates/oauth2-redirect.html index 879e177b0..214f39351 100644 --- a/apps/docs/templates/oauth2-redirect.html +++ b/apps/docs/templates/oauth2-redirect.html @@ -8,6 +8,7 @@