-
Notifications
You must be signed in to change notification settings - Fork 0
Multitenant: Add filtering by tenant_id #282
Conversation
…e login, and hide navbar on login since it uses some fields from applicationSettings Note during rebase: originially I'd killed `loadApplicationSettings` in favor of `login` calling `doFetch('application_settings')`, but #249 added new logic to `loadApplicationSettings` so had to bring it back during rebase. We still do NOT call it before login in main.js anymore.
Rebase note: formerly these functions were in db/index
…lly omitted during interactive rebase
…environment variable
…ities by ID (mainly uploads)
…t on the request object
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Directionally, this looks great. Feel free to leave some of these TODOs in the code for @igor47 and I to resolve.
Here are my thoughts on how to adapt seeds:
We currently use db seed files primarily for unit tests and local execution. These shouldn't run in any production or staging environments. From that point of view, whatever changes we make to seed files should align to the goal of giving developers a basically usable configuration after running.
One approach could be to set initial users, reporting periods and application settings to the default tenant_id.
…nts (new after merge)
…en reworked and tenant_id checks readded in 0f574f8
Github seems to have done some confusing thing with my stacked PRs... Since #259 was merged, it says:
But then there are commits I've pushed to this branch past Specifically: |
…st during git merge
…and reporting_periods
…ntually removing the default value from the column
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
left comments of varying pertinence throughout the diff here, but nothing is blocking. maybe just go through my comments and make sure nothing jumps out at you?
@@ -1,22 +1,27 @@ | |||
require('dotenv').config() | |||
|
|||
exports.seed = async function (knex) { | |||
// NOTE(mbroussard): unclear to me if this seed will be used again, but I'm updating it to minimally |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it probably won't get used again; we should maybe just drop this file, and also the migration script in scripts
esp now that we have a blueprint
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. I'm probably not going to do that in this PR, but if you'd like to kill it separately, go ahead 👍
} else { | ||
await store.dispatch('updateApplicationSettings') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Answered above and in PR description)
src/server/db/agencies.js
Outdated
@@ -6,8 +6,13 @@ function baseQuery (trns) { | |||
.select('*') | |||
} | |||
|
|||
function agencies (trns = knex) { | |||
function agencies (tenantId, trns = knex) { | |||
if (tenantId === undefined) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we explicitly test for undefined
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the default tenant ID is 0
which is falsey, so if (tenantId)
is not enough.
@@ -33,10 +34,19 @@ router.post('/', requireAdminUser, async function (req, res, next) { | |||
|
|||
try { | |||
if (agencyInfo.id) { | |||
const existingAgency = await agencyById(agencyInfo.id) | |||
if (!existingAgency || existingAgency.tenant_id !== req.session.user.tenant_id) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
huh, how would the second condition ever happen?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Someone could do a request passing in an agency ID that is valid but refers to an agency of a different tenant.
@@ -4,11 +4,17 @@ const _ = require('lodash') | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't think the exports routes are in-use
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, the code exists still and it theoretically could be called, so it needs to obey multitenant rules. If you want to kill it after this PR merges, that's cool 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, it does seem in use: the main "Download Treasury Report" button on the dashboard links to /api/exports
…that fails since we removed the projects seed
…an equivalent one
Depends on #259
See overall GOST integration plan: https://www.notion.so/usdr/GOST-ARPA-integration-technical-plan-cffc5800c0414660816a0920b959da82#b4dfb0449fc54674ae97f070a6b7b33a
This PR adds filtering and access checks based on the current user's
tenant_id
in (as far as I know) all places that need it. In most places the tenant ID to filter by comes from the logged in user, via a newreq.session.user
object populated byrequireUser
middleware that's used almost everywhere. In a few cases, functions that deal with other types of entities (mainly uploads) get a tenant ID from that object.Most changes are serverside only and clients are generally not aware of the concept of tenants. My approach was to go through all the DB accessor functions in src/server/db and trace backward to the places that call them. This was a bit tricky in the absence of TypeScript since I had to rely on grep and we do some annoying things like renaming exports and imports.
One notable clientside change is that previously
/api/application_settings
was called before logging in and thus could not userequireUser
to determine a tenant ID to filter by. I've changed this so that this data is not loaded until after login now, and the endpoint requires authentication. Accordingly, the navbar with the period dropdown (which draws from this data) is now hidden before login. There is a slight race condition after logging in since nothing explicitly waits forapplication_settings
to be populated, but the only apparent issue in the gap before it loads is that the period selector is not populated. This doesn't seem like a huge deal, and that gap is very short.Regarding dead code:
routes/files
and all code referencingUPLOAD_DIRECTORY
. This seemed to be dead (posted about it on Slack here) and was problematic to keep around because it doesn't interact with the database and couldn't be easily tenant-scoped. Also, the implementation of the /api/files/delete endpoint was insecure and could have been used to arbitrarily delete any file on the machine!I did not attempt for now to add filtering logic inedit: now that Igor/recipients #260 has merged, I did go back and add tenantId checks to code dealing with the revamped subrecipients model.db/subrecipients
orroutes/subrecipients
since there is a concurrent PR Igor/recipients #260 that is removing/revamping these. I had previously made changes to several other now-deleted files that caused some issues when rebasing over Additional UI for upload tracking #249For DB seeds, the "dev" ones (which I think may also be used in prod deployments? Staging at least has data that looks like it came from them) still assume a single tenant with ID
0
. Some of the "test" seeds add data for multiple tenants so that the tests can verify multitenant behaviors.In this PR I also added a mechanism for writing API route tests using Supertest (this is conceptually similar to how API route tests work in GOST but imo a bit cleaner and something we might want to move GOST over to eventually). I added a few route tests verifying this works (health & sessions) and a few (exports) for multitenant, and I'd like to add some more, but I'm going to try to put those in a separate PR so this one stops growing and can land sooner.