diff --git a/.gcloudignore b/.gcloudignore index f6ced6bf79bd..f9b543c15b3f 100644 --- a/.gcloudignore +++ b/.gcloudignore @@ -49,3 +49,6 @@ tutorial-env # Testdata for Python tests testdata + +# API Key for local development +ot_api_key.txt diff --git a/.gitignore b/.gitignore index b34058c8d02c..b38d9d0910a7 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,6 @@ coverage # venv directory cs-env + +# API Key for local development +ot_api_key.txt diff --git a/README.md b/README.md index ba326c423208..cfb79824b0d6 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,14 @@ This will start a local datastore emulator, run unit tests, and then shut down t There are some developing information in developer-documentation.md. +### Origin Trials +To test the functionality of this application locally that interacts with data from the Origin Trials API, an API key will need to be acquired. To do this, run the following command: + +```bash +npm run dev-ot-key +``` + +Note: *Only developers with access to the cr-status-staging GCP project will be able to successfully run this command. If you need to test this and you don't have access, open an issue.* **Notes** diff --git a/framework/secrets.py b/framework/secrets.py index 11f7bf70f61c..cc442f4bde83 100644 --- a/framework/secrets.py +++ b/framework/secrets.py @@ -17,6 +17,7 @@ import hmac import logging import random +import settings import string import time @@ -27,6 +28,7 @@ RANDOM_KEY_LENGTH = 128 RANDOM_KEY_CHARACTERS = string.ascii_letters + string.digits +ot_api_key: str|None = None def make_random_key(length=RANDOM_KEY_LENGTH, chars=RANDOM_KEY_CHARACTERS): """Return a string with lots of random characters.""" @@ -121,3 +123,31 @@ def record_failure(self, now=None) -> None: logging.info('Recording failure at %r', now or int(time.time())) self.failure_timestamp = now or int(time.time()) self.put() + + +def get_ot_api_key() -> str|None: + """Obtain an API key to be used for requests to the origin trials API.""" + # Reuse the API key's value if we've already obtained it. + if settings.OT_API_KEY is not None: + return settings.OT_API_KEY + + if settings.DEV_MODE or settings.UNIT_TEST_MODE: + # In dev or unit test mode, pull the API key from a local file. + try: + with open(f'{settings.ROOT_DIR}/ot_api_key.txt', 'r') as f: + settings.OT_API_KEY = f.read().strip() + return settings.OT_API_KEY + except: + logging.info('No key found locally for the Origin Trials API.') + return None + else: + # If in staging or prod, pull the API key from the project secrets. + from google.cloud.secretmanager import SecretManagerServiceClient + client = SecretManagerServiceClient() + name = (f'{client.secret_path(settings.APP_ID, "OT_API_KEY")}' + '/versions/latest') + response = client.access_secret_version(request={'name': name}) + if response: + settings.OT_API_KEY = response.payload.data.decode("UTF-8") + return settings.OT_API_KEY + return None diff --git a/package.json b/package.json index bcaa9071bf63..dacda6a3d7e0 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "clean-setup": "rm -rf node_modules cs-env; npm run setup", "deps": "source cs-env/bin/activate; pip install -r requirements.txt --upgrade; pip install -r requirements.dev.txt --upgrade", "dev-deps": "echo 'dev-deps is no longer needed'", + "dev-ot-key": "gcloud secrets versions access latest --secret=DEV_OT_API_KEY --out-file=ot_api_key.txt --project=cr-status-staging", "do-tests": "source cs-env/bin/activate; curl -X POST 'http://localhost:15606/reset' && python3.11 -m unittest discover -p '*_test.py' -b", "start-emulator-persist": "gcloud beta emulators datastore start --host-port=:15606 --consistency=1.0", "start-emulator": "gcloud beta emulators datastore start --host-port=:15606 --no-store-on-disk --consistency=1.0", diff --git a/requirements.txt b/requirements.txt index 75b504b396b0..b73c5cdbf988 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,6 +14,7 @@ google-api-python-client==2.47.0 google-cloud-tasks==2.7.0 google-cloud-ndb==1.11.1 google-cloud-logging==3.6.0 +google-cloud-secret-manager==2.16.2 google-auth==1.31.0 requests==2.31.0 redis==4.4.4 diff --git a/settings.py b/settings.py index 292f852576c0..908d99b38c9e 100644 --- a/settings.py +++ b/settings.py @@ -1,6 +1,7 @@ import logging import os -from typing import Any, Optional + +from framework.secrets import get_ot_api_key ROOT_DIR = os.path.abspath(os.path.dirname(__file__)) @@ -12,7 +13,7 @@ def get_flask_template_path() -> str: # By default, send all email to an archive for debugging. # For the live cr-status server, this setting is None. -SEND_ALL_EMAIL_TO: Optional[str] = ( +SEND_ALL_EMAIL_TO: str|None = ( 'cr-status-staging-emails+%(user)s+%(domain)s@google.com') BOUNCE_ESCALATION_ADDR = 'cr-status-bounces@google.com' @@ -74,6 +75,9 @@ def get_flask_template_path() -> str: # Truncate some log lines to stay under limits of Google Cloud Logging. MAX_LOG_LINE = 200 * 1000 +# Origin trials API URL +OT_API_URL = 'https://staging-chromeorigintrials-pa.sandbox.googleapis.com' +OT_API_KEY: str|None = None # Value is set later when request is needed. if UNIT_TEST_MODE: APP_TITLE = 'Local testing' @@ -89,6 +93,7 @@ def get_flask_template_path() -> str: SEND_EMAIL = True SEND_ALL_EMAIL_TO = None # Deliver it to the intended users SITE_URL = 'https://chromestatus.com/' + OT_API_URL = 'https://chromeorigintrials-pa.googleapis.com' GOOGLE_SIGN_IN_CLIENT_ID = ( '999517574127-7ueh2a17bv1ave9thlgtap19pt5qjp4g.' 'apps.googleusercontent.com')