Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authenticate endpoints and validate user state_code in metrics requests #546

Merged
merged 7 commits into from
May 5, 2022

Conversation

terryttsai
Copy link
Contributor

@terryttsai terryttsai commented May 4, 2022

Description of the change

Authenticate endpoints when auth is enabled and validate that user state_code matches the requested tenantId in metrics requests.

This ensures that users with a state_code in one state cannot request data from another state.

Users with a state_code of recidiviz can request data from any state.

Type of change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Configuration change (adjusts configuration to achieve some end related to functionality, development, performance, or security)

Related issues

Closes #540

Checklists

Development

These boxes should be checked by the submitter prior to merging:

  • Manual testing against realistic data has been performed locally

Code review

These boxes should be checked by reviewers prior to merging:

  • This pull request has a descriptive title and information useful to a reviewer
  • This pull request has been moved out of a Draft state, has no "Work In Progress" label, and has assigned reviewers
  • Potential security implications or infrastructural changes have been considered, if relevant

@@ -44,6 +44,8 @@ Expected backend environment variables include:

- `GOOGLE_APPLICATION_CREDENTIALS` - a relative path pointing to the JSON file containing the credentials of the service account used to communicate with Google Cloud Storage, for metric retrieval.
- `METRIC_BUCKET` - the name of the Google Cloud Storage bucket where the metrics reside.
- `AUTH_ENABLED` - whether or not we should require authentication to access our endpoints. Currently only used in staging to make the entire site private. No need to enable this locally unless you are developing or testing something auth-related. If set to `true` then `AUTH0_APP_METADATA_KEY` **must** be set to a supported value.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means we'll need to change the staging environment to make sure these keys are set

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea good call, do you know where/how to do this? I'm actually not sure, I forget where the Spotlight backend is hosted. I see something both in Firebase and also GCP.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the backend is in App Engine, there are GAE config files in a shared 1password document that we have to update and then store locally (untracked) in order to deploy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@colincadams do you know how to set the staging environment variables?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@terryttsai @macfarlandian so is it sufficient to update gae-staging.yaml in public-dashboard-untracked-config-files.zip in 1pass? How do those get pulled in during deploy?

@terryttsai you don't have access to 1pass, right? So I can do this for you

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so, I don't see that file in 1pass

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that zip file is the "master copy" but then you have to download it and copy those files into their respective locations in your working directory. (The deployed backend does not actually use the .env file, which is a little confusing, it uses the environment config in the yaml for that environment. The .env is just for development.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Are spotlight deploys just done manually by whomever wants to then? By running gcloud app deploy gae-staging.yaml --project [staging_project_id]? Or is there a more formal process?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope that's as formal as it gets

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, ok this all makes sense, thanks!

Copy link
Contributor

@lilidworkin lilidworkin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good to me, thanks for doing the backend component of this too @terryttsai just to be super safe!

@@ -44,6 +44,8 @@ Expected backend environment variables include:

- `GOOGLE_APPLICATION_CREDENTIALS` - a relative path pointing to the JSON file containing the credentials of the service account used to communicate with Google Cloud Storage, for metric retrieval.
- `METRIC_BUCKET` - the name of the Google Cloud Storage bucket where the metrics reside.
- `AUTH_ENABLED` - whether or not we should require authentication to access our endpoints. Currently only used in staging to make the entire site private. No need to enable this locally unless you are developing or testing something auth-related. If set to `true` then `AUTH0_APP_METADATA_KEY` **must** be set to a supported value.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea good call, do you know where/how to do this? I'm actually not sure, I forget where the Spotlight backend is hosted. I see something both in Firebase and also GCP.

stateCode !== tenantId.toLowerCase() &&
stateCode !== "recidiviz"
) {
res.status(401).json({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should throw different error messages in these different cases:

  • AUTH0_APP_METADATA_KEY is empty
  • req.user?.[AUTH0_APP_METADATA_KEY] is empty
  • req.user?.[AUTH0_APP_METADATA_KEY]?.state_code is empty
  • req.user?.[AUTH0_APP_METADATA_KEY]?.state_code is set, but not equal to the tenant ID

Might be helpful for debugging?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I can assert that AUTH0_APP_METADATA_KEY is defined and we'll see the error in the logs if it's not? If any of the rest is empty we know the user app_metadata hasn't been configured properly. We could have different logs though I would want to return the same 401 error to the client.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep sounds good, whatever you think is best! Just thought I'd point it out

@@ -44,6 +44,8 @@ Expected backend environment variables include:

- `GOOGLE_APPLICATION_CREDENTIALS` - a relative path pointing to the JSON file containing the credentials of the service account used to communicate with Google Cloud Storage, for metric retrieval.
- `METRIC_BUCKET` - the name of the Google Cloud Storage bucket where the metrics reside.
- `AUTH_ENABLED` - whether or not we should require authentication to access our endpoints. Currently only used in staging to make the entire site private. No need to enable this locally unless you are developing or testing something auth-related. If set to `true` then `AUTH0_APP_METADATA_KEY` **must** be set to a supported value.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the backend is in App Engine, there are GAE config files in a shared 1password document that we have to update and then store locally (untracked) in order to deploy.

spotlight-api/.env.example Outdated Show resolved Hide resolved
Copy link
Collaborator

@macfarlandian macfarlandian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tested this locally and it looks great! just had a few minor notes

spotlight-api/routes/api.test.js Outdated Show resolved Hide resolved
@coveralls
Copy link

coveralls commented May 4, 2022

Pull Request Test Coverage Report for Build 2272685546

Warning: This coverage report may be inaccurate.

This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.

Details

  • 7 of 7 (100.0%) changed or added relevant lines in 3 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall first build on terry/auth-endpoint-by-user-state-code at 84.797%

Totals Coverage Status
Change from base Build 2247875950: 84.8%
Covered Lines: 2008
Relevant Lines: 2248

💛 - Coveralls

@@ -30,6 +30,7 @@ type ErrorAPIResponse = { error: string };
type FetchMetricOptions = {
metricNames: string[];
tenantId: TenantId;
getToken: () => Promise<string>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what was the issue here, importing the datastore into this module was causing some kind of dependency resolution problem? I wonder if it might be better solved by moving the API methods into a datastore class rather than drilling this function through all of our content models? (I don't know off the top of my head if that would cause different problems but it seems like it might be cleaner, if it works)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I was going to ask here, if it would be easy to move the API methods into one of our stores and call it without having to pass it down all these content models?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because I was getting nasty dependency cycles with trying to import the Datastore in fetchmetrics

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should give it a try! IIRC I kept them separate because at the time they didn't really have any dependencies, but there isn't any structural reason why they have to be set up that way

@terryttsai terryttsai force-pushed the terry/auth-endpoint-by-user-state-code branch from efffd4f to 552814e Compare May 5, 2022 00:01
@terryttsai terryttsai merged commit e571ade into main May 5, 2022
@terryttsai terryttsai deleted the terry/auth-endpoint-by-user-state-code branch May 5, 2022 17:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement state-specific staging access
4 participants