Skip to content

Commit 0afe257

Browse files
authored
feat(auth): add tenant boundaries to auth GraphQL resolvers (#3697)
* feat(auth): add migration to seed operators api secret * feat(auth): propagate apiSecret during tenant creation * test(auth): update api secret tests & tableManager * chore(localenv): update ADMIN_API_SECRET in docker compose files * chore(bruno): update authApiSignatureSecret in requests * feat(auth): add getTenantFromApiSignature middleware * chore(auth): move tenant signature functions to separate file * feat(auth): update authed tenant middleware, and add tests * test(backend): update tenant test * chore(testenv): add ADMIN_API_SECRET to auth * feat(backend): call the auth service client if apiSecret has been updated * test(auth): update tenant signature test name * feat(auth): update tenant response from auth api to include the apiSecet * test(auth): pull out apolloClient creation from test app * feat(auth): add tenantId to GraphQL schema * feat(auth): add tenant boundaries to resolvers * feat(auth): disconnect from redis on app shutdown * test(auth): add additional tests for tenant signature verification
1 parent 0c08e41 commit 0afe257

File tree

9 files changed

+595
-120
lines changed

9 files changed

+595
-120
lines changed

packages/auth/src/grant/service.test.ts

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,23 @@ describe('Grant Service', (): void => {
317317
expect(fetchedGrant?.id).toEqual(grant.id)
318318
expect(fetchedGrant?.access?.length).toBeGreaterThan(0)
319319
})
320+
321+
test('Can filter by tenantId', async () => {
322+
const fetchedGrant = await grantService.getByIdWithAccess(
323+
grant.id,
324+
grant.tenantId
325+
)
326+
expect(fetchedGrant?.id).toEqual(grant.id)
327+
expect(fetchedGrant?.access?.length).toBeGreaterThan(0)
328+
})
329+
330+
test('Returns undefined if incorrect tenantId', async () => {
331+
const fetchedGrant = await grantService.getByIdWithAccess(
332+
grant.id,
333+
v4()
334+
)
335+
expect(fetchedGrant).toBeUndefined()
336+
})
320337
})
321338

322339
describe('finalize', (): void => {
@@ -423,18 +440,33 @@ describe('Grant Service', (): void => {
423440
const walletAddress = 'example.com/test'
424441

425442
beforeEach(async () => {
443+
const secondTenant = await Tenant.query().insertAndFetch(generateTenant())
426444
const grantDetails = [
427445
{
428446
identifier: walletAddress,
429447
state: GrantState.Finalized,
430-
finalizationReason: GrantFinalization.Revoked
448+
finalizationReason: GrantFinalization.Revoked,
449+
tenantId: tenant.id
431450
},
432-
{ identifier: walletAddress, state: GrantState.Pending },
433-
{ identifier: 'example.com/test3', state: GrantState.Pending }
451+
{
452+
identifier: walletAddress,
453+
state: GrantState.Pending,
454+
tenantId: tenant.id
455+
},
456+
{
457+
identifier: 'example.com/test3',
458+
state: GrantState.Pending,
459+
tenantId: secondTenant.id
460+
}
434461
]
435462

436-
for (const { identifier, state, finalizationReason } of grantDetails) {
437-
const grant = await createGrant(deps, tenant.id, { identifier })
463+
for (const {
464+
identifier,
465+
state,
466+
finalizationReason,
467+
tenantId
468+
} of grantDetails) {
469+
const grant = await createGrant(deps, tenantId, { identifier })
438470
const updatedGrant = await grant
439471
.$query()
440472
.patchAndFetch({ state, finalizationReason })
@@ -542,5 +574,17 @@ describe('Grant Service', (): void => {
542574
})
543575
})
544576
})
577+
578+
test('Can filter by tenantId', async (): Promise<void> => {
579+
const page = await grantService.getPage(
580+
undefined,
581+
undefined,
582+
undefined,
583+
tenant.id
584+
)
585+
586+
expect(page.length).toBe(2)
587+
expect(page.every((result) => result.tenantId === tenant.id)).toBeTruthy()
588+
})
545589
})
546590
})

packages/auth/src/grant/service.ts

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ interface GrantFilter {
2525
}
2626

2727
export interface GrantService {
28-
getByIdWithAccess(grantId: string): Promise<Grant | undefined>
28+
getByIdWithAccess(
29+
grantId: string,
30+
tenantId?: string
31+
): Promise<Grant | undefined>
2932
create(
3033
grantRequest: GrantRequest,
3134
tenantId: string,
@@ -43,7 +46,8 @@ export interface GrantService {
4346
getPage(
4447
pagination?: Pagination,
4548
filter?: GrantFilter,
46-
sortOrder?: SortOrder
49+
sortOrder?: SortOrder,
50+
tenantId?: string
4751
): Promise<Grant[]>
4852
updateLastContinuedAt(id: string): Promise<Grant>
4953
lock(grantId: string, trx: Transaction, timeoutMs?: number): Promise<void>
@@ -120,7 +124,8 @@ export async function createGrantService({
120124
knex
121125
}
122126
return {
123-
getByIdWithAccess: (grantId: string) => getByIdWithAccess(grantId),
127+
getByIdWithAccess: (grantId: string, tenantId?: string) =>
128+
getByIdWithAccess(grantId, tenantId),
124129
create: (grantRequest: GrantRequest, tenantId: string, trx?: Transaction) =>
125130
create(deps, grantRequest, tenantId, trx),
126131
markPending: (grantId: string, trx?: Transaction) =>
@@ -134,16 +139,25 @@ export async function createGrantService({
134139
) => getByContinue(continueId, continueToken, opts),
135140
revokeGrant: (grantId: string, tenantId?: string) =>
136141
revokeGrant(deps, grantId, tenantId),
137-
getPage: (pagination?, filter?, sortOrder?) =>
138-
getGrantsPage(deps, pagination, filter, sortOrder),
142+
getPage: (pagination?, filter?, sortOrder?, tenantId?) =>
143+
getGrantsPage(deps, pagination, filter, sortOrder, tenantId),
139144
updateLastContinuedAt: (id) => updateLastContinuedAt(id),
140145
lock: (grantId: string, trx: Transaction, timeoutMs?: number) =>
141146
lock(deps, grantId, trx, timeoutMs)
142147
}
143148
}
144149

145-
async function getByIdWithAccess(grantId: string): Promise<Grant | undefined> {
146-
return Grant.query().findById(grantId).withGraphJoined('access')
150+
async function getByIdWithAccess(
151+
grantId: string,
152+
tenantId?: string
153+
): Promise<Grant | undefined> {
154+
const query = Grant.query().findById(grantId).withGraphJoined('access')
155+
156+
if (tenantId) {
157+
query.where('tenantId', tenantId)
158+
}
159+
160+
return query
147161
}
148162

149163
async function approve(grantId: string): Promise<Grant> {
@@ -307,7 +321,8 @@ async function getGrantsPage(
307321
deps: ServiceDependencies,
308322
pagination?: Pagination,
309323
filter?: GrantFilter,
310-
sortOrder?: SortOrder
324+
sortOrder?: SortOrder,
325+
tenantId?: string
311326
): Promise<Grant[]> {
312327
const query = Grant.query(deps.knex).withGraphJoined('access')
313328
const { identifier, state, finalizationReason } = filter ?? {}
@@ -334,6 +349,10 @@ async function getGrantsPage(
334349
.orWhereNotIn('finalizationReason', finalizationReason.notIn)
335350
}
336351

352+
if (tenantId) {
353+
query.where('tenantId', tenantId)
354+
}
355+
337356
return query.getPage(pagination, sortOrder)
338357
}
339358

packages/auth/src/graphql/generated/graphql.schema.json

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/auth/src/graphql/generated/graphql.ts

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)