Skip to content

Commit

Permalink
feat(auth): tenanted grants (#3187)
Browse files Browse the repository at this point in the history
* feat(auth): tenanted grants

* fix: tests

* feat: update bruno collection

* fix: tests

* feat: update bruno requests, fix integration tests

* feat: handle tenants with no idp info

* feat: backfill tenants on grants, trim down queries

* feat: use tenantId in grant revocation

* fix: service function signatures
  • Loading branch information
njlie authored Feb 11, 2025
1 parent 4a4a48d commit f8a61c4
Show file tree
Hide file tree
Showing 41 changed files with 843 additions and 272 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ script:post-response {
authUrl.hostname.includes('happy-life-bank')
){
const port = authUrl.hostname.includes('cloud-nine-wallet')? authUrl.port: Number(authUrl.port) + 1000
bru.setEnvVar("receiverOpenPaymentsAuthHost", authUrl.protocol + '//localhost:' + port );
bru.setEnvVar("receiverOpenPaymentsAuthHost", authUrl.protocol + '//localhost:' + port + authUrl.path );
} else {
bru.setEnvVar("receiverOpenPaymentsAuthHost", body?.authServer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ script:post-response {
}

const body = res.getBody()

bru.setEnvVar("senderAssetCode", body?.assetCode)
bru.setEnvVar("senderAssetScale", body?.assetScale)

Expand All @@ -38,7 +38,7 @@ script:post-response {
authUrl.hostname.includes('happy-life-bank')
){
const port = authUrl.hostname.includes('cloud-nine-wallet')? authUrl.port: Number(authUrl.port) + 1000
bru.setEnvVar("senderOpenPaymentsAuthHost", authUrl.protocol + '//localhost:' + port );
bru.setEnvVar("senderOpenPaymentsAuthHost", authUrl.protocol + '//localhost:' + port + authUrl.path );
} else {
bru.setEnvVar("senderOpenPaymentsAuthHost", body?.authServer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ meta {
}

post {
url: {{receiverOpenPaymentsAuthHost}}/
url: {{receiverOpenPaymentsAuthHost}}
body: json
auth: none
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ meta {
}

post {
url: {{senderOpenPaymentsAuthHost}}/
url: {{senderOpenPaymentsAuthHost}}
body: json
auth: none
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ script:pre-request {

script:post-response {
const url = require('url')

if (res.getStatus() !== 200) {
return
}

const body = res.getBody()
bru.setEnvVar("receiverAssetCode", body?.assetCode)
bru.setEnvVar("receiverAssetScale", body?.assetScale)
Expand All @@ -37,7 +37,7 @@ script:post-response {
authUrl.hostname.includes('happy-life-bank')
){
const port = authUrl.hostname.includes('cloud-nine-wallet')? authUrl.port: Number(authUrl.port) + 1000
bru.setEnvVar("receiverOpenPaymentsAuthHost", authUrl.protocol + '//localhost:' + port );
bru.setEnvVar("receiverOpenPaymentsAuthHost", authUrl.protocol + '//localhost:' + port + authUrl.path);
} else {
bru.setEnvVar("receiverOpenPaymentsAuthHost", body?.authServer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ script:post-response {
authUrl.hostname.includes('happy-life-bank')
){
const port = authUrl.hostname.includes('cloud-nine-wallet')? authUrl.port: Number(authUrl.port) + 1000
bru.setEnvVar("senderOpenPaymentsAuthHost", authUrl.protocol + '//localhost:' + port );
bru.setEnvVar("senderOpenPaymentsAuthHost", authUrl.protocol + '//localhost:' + port + authUrl.path );
} else {
bru.setEnvVar("senderOpenPaymentsAuthHost", body?.authServer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ meta {
}

post {
url: {{receiverOpenPaymentsAuthHost}}/
url: {{receiverOpenPaymentsAuthHost}}
body: json
auth: none
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ meta {
}

post {
url: {{senderOpenPaymentsAuthHost}}/
url: {{senderOpenPaymentsAuthHost}}
body: json
auth: none
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ meta {
}

post {
url: {{senderOpenPaymentsAuthHost}}/
url: {{senderOpenPaymentsAuthHost}}
body: json
auth: none
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ script:post-response {
}

const body = res.getBody()

bru.setEnvVar("receiverAssetCode", body?.assetCode)
bru.setEnvVar("receiverAssetScale", body?.assetScale)

Expand All @@ -38,7 +38,7 @@ script:post-response {
authUrl.hostname.includes('happy-life-bank')
){
const port = authUrl.hostname.includes('cloud-nine-wallet')? authUrl.port: Number(authUrl.port) + 1000
bru.setEnvVar("receiverOpenPaymentsAuthHost", authUrl.protocol + '//localhost:' + port );
bru.setEnvVar("receiverOpenPaymentsAuthHost", authUrl.protocol + '//localhost:' + port + authUrl.path );
} else {
bru.setEnvVar("receiverOpenPaymentsAuthHost", body?.authServer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ script:post-response {
authUrl.hostname.includes('happy-life-bank')
){
const port = authUrl.hostname.includes('cloud-nine-wallet')? authUrl.port: Number(authUrl.port) + 1000
bru.setEnvVar("senderOpenPaymentsAuthHost", authUrl.protocol + '//localhost:' + port );
bru.setEnvVar("senderOpenPaymentsAuthHost", authUrl.protocol + '//localhost:' + port + authUrl.path );
} else {
bru.setEnvVar("senderOpenPaymentsAuthHost", body?.authServer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ meta {
}

post {
url: {{receiverOpenPaymentsAuthHost}}/
url: {{receiverOpenPaymentsAuthHost}}
body: json
auth: none
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ meta {
}

post {
url: {{senderOpenPaymentsAuthHost}}/
url: {{senderOpenPaymentsAuthHost}}
body: json
auth: none
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ meta {
}

post {
url: {{receiverOpenPaymentsAuthHost}}/
url: {{receiverOpenPaymentsAuthHost}}
body: json
auth: none
}
Expand Down
1 change: 1 addition & 0 deletions bruno/collections/Rafiki/environments/Local Playground.bru
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ vars {
assetCode: USD
assetScale: 2
senderTenantId: 438fa74a-fa7d-4317-9ced-dde32ece1787
RafikiGraphqlHostTenantId: 438fa74a-fa7d-4317-9ced-dde32ece1787
}
30 changes: 30 additions & 0 deletions packages/auth/migrations/20241206232423_add_tenant_to_grant.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = function (knex) {
return knex.schema
.alterTable('grants', function (table) {
table.uuid('tenantId').references('tenants.id').index()
})
.then(() => {
return knex.raw(
`UPDATE "grants" SET "tenantId" = (SELECT id from "tenants" LIMIT 1)`
)
})
.then(() => {
return knex.schema.alterTable('grants', (table) => {
table.uuid('tenantId').notNullable().alter()
})
})
}

/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function (knex) {
return knex.schema.alterTable('grants', function (table) {
table.dropColumn('tenantId')
})
}
24 changes: 8 additions & 16 deletions packages/auth/src/access/service.test.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { faker } from '@faker-js/faker'
import nock from 'nock'
import { Knex } from 'knex'
import { v4 } from 'uuid'
import { createTestApp, TestContainer } from '../tests/app'
import { truncateTables } from '../tests/tableManager'
import { Config } from '../config/app'
import { IocContract } from '@adonisjs/fold'
import { initIocContainer } from '../'
import { AppServices } from '../app'
import { AccessService } from './service'
import { Grant, GrantState, StartMethod, FinishMethod } from '../grant/model'
import { Grant } from '../grant/model'
import { IncomingPaymentRequest, OutgoingPaymentRequest } from './types'
import { generateNonce, generateToken } from '../shared/utils'
import { generateBaseGrant } from '../tests/grant'
import { AccessType, AccessAction } from '@interledger/open-payments'
import { Access } from './model'
import { Tenant } from '../tenant/model'
import { generateTenant } from '../tests/tenant'

describe('Access Service', (): void => {
let deps: IocContract<AppServices>
Expand All @@ -22,19 +22,11 @@ describe('Access Service', (): void => {
let trx: Knex.Transaction
let grant: Grant

const generateBaseGrant = () => ({
state: GrantState.Pending,
startMethod: [StartMethod.Redirect],
continueToken: generateToken(),
continueId: v4(),
finishMethod: FinishMethod.Redirect,
finishUri: 'https://example.com/finish',
clientNonce: generateNonce(),
client: faker.internet.url({ appendSlash: false })
})

beforeEach(async (): Promise<void> => {
grant = await Grant.query(trx).insertAndFetch(generateBaseGrant())
const tenant = await Tenant.query(trx).insertAndFetch(generateTenant())
grant = await Grant.query(trx).insertAndFetch(
generateBaseGrant({ tenantId: tenant.id })
)
})

beforeAll(async (): Promise<void> => {
Expand Down
10 changes: 8 additions & 2 deletions packages/auth/src/access/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { createTestApp, TestContainer } from '../tests/app'
import { truncateTables } from '../tests/tableManager'
import { generateToken, generateNonce } from '../shared/utils'
import { compareRequestAndGrantAccessItems } from './utils'
import { Tenant } from '../tenant/model'
import { generateTenant } from '../tests/tenant'

describe('Access utilities', (): void => {
let deps: IocContract<AppServices>
Expand All @@ -25,6 +27,7 @@ describe('Access utilities', (): void => {
let identifier: string
let grant: Grant
let grantAccessItem: Access
let tenant: Tenant

const receiver: string =
'https://wallet.com/alice/incoming-payments/12341234-1234-1234-1234-123412341234'
Expand All @@ -36,6 +39,7 @@ describe('Access utilities', (): void => {

beforeEach(async (): Promise<void> => {
identifier = `https://example.com/${v4()}`
tenant = await Tenant.query(trx).insertAndFetch(generateTenant())
grant = await Grant.query(trx).insertAndFetch({
state: GrantState.Processing,
startMethod: [StartMethod.Redirect],
Expand All @@ -44,7 +48,8 @@ describe('Access utilities', (): void => {
finishMethod: FinishMethod.Redirect,
finishUri: 'https://example.com/finish',
clientNonce: generateNonce(),
client: faker.internet.url({ appendSlash: false })
client: faker.internet.url({ appendSlash: false }),
tenantId: tenant.id
})

grantAccessItem = await Access.query(trx).insertAndFetch({
Expand Down Expand Up @@ -241,7 +246,8 @@ describe('Access utilities', (): void => {
finishMethod: FinishMethod.Redirect,
finishUri: 'https://example.com/finish',
clientNonce: generateNonce(),
client: faker.internet.url({ appendSlash: false })
client: faker.internet.url({ appendSlash: false }),
tenantId: tenant.id
})

const grantAccessItem = await Access.query(trx).insertAndFetch({
Expand Down
20 changes: 17 additions & 3 deletions packages/auth/src/accessToken/routes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import {
import { GrantService } from '../grant/service'
import { AccessTokenService } from './service'
import { GNAPErrorCode } from '../shared/gnapErrors'
import { generateTenant } from '../tests/tenant'
import { Tenant } from '../tenant/model'

describe('Access Token Routes', (): void => {
let deps: IocContract<AppServices>
Expand Down Expand Up @@ -96,7 +98,11 @@ describe('Access Token Routes', (): void => {
const method = 'POST'

beforeEach(async (): Promise<void> => {
grant = await Grant.query(trx).insertAndFetch(BASE_GRANT)
const tenant = await Tenant.query().insertAndFetch(generateTenant())
grant = await Grant.query(trx).insertAndFetch({
...BASE_GRANT,
tenantId: tenant.id
})
access = await Access.query(trx).insertAndFetch({
grantId: grant.id,
...BASE_ACCESS
Expand Down Expand Up @@ -367,7 +373,11 @@ describe('Access Token Routes', (): void => {
let token: AccessToken

beforeEach(async (): Promise<void> => {
grant = await Grant.query(trx).insertAndFetch(BASE_GRANT)
const tenant = await Tenant.query().insertAndFetch(generateTenant())
grant = await Grant.query(trx).insertAndFetch({
...BASE_GRANT,
tenantId: tenant.id
})
token = await AccessToken.query(trx).insertAndFetch({
grantId: grant.id,
...BASE_TOKEN
Expand Down Expand Up @@ -406,7 +416,11 @@ describe('Access Token Routes', (): void => {
let token: AccessToken

beforeEach(async (): Promise<void> => {
grant = await Grant.query(trx).insertAndFetch(BASE_GRANT)
const tenant = await Tenant.query(trx).insertAndFetch(generateTenant())
grant = await Grant.query(trx).insertAndFetch({
...BASE_GRANT,
tenantId: tenant.id
})
access = await Access.query(trx).insertAndFetch({
grantId: grant.id,
...BASE_ACCESS
Expand Down
13 changes: 11 additions & 2 deletions packages/auth/src/accessToken/service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import {
AccessItem
} from '@interledger/open-payments'
import { generateBaseGrant } from '../tests/grant'
import { Tenant } from '../tenant/model'
import { generateTenant } from '../tests/tenant'

describe('Access Token Service', (): void => {
let deps: IocContract<AppServices>
Expand Down Expand Up @@ -63,8 +65,9 @@ describe('Access Token Service', (): void => {

let grant: Grant
beforeEach(async (): Promise<void> => {
const tenant = await Tenant.query(trx).insertAndFetch(generateTenant())
grant = await Grant.query(trx).insertAndFetch(
generateBaseGrant({ state: GrantState.Approved })
generateBaseGrant({ state: GrantState.Approved, tenantId: tenant.id })
)
grant.access = [
await Access.query(trx).insertAndFetch({
Expand Down Expand Up @@ -186,8 +189,9 @@ describe('Access Token Service', (): void => {
})

test('Introspection only returns requested access', async (): Promise<void> => {
const tenant = await Tenant.query(trx).insertAndFetch(generateTenant())
const grantWithTwoAccesses = await Grant.query(trx).insertAndFetch(
generateBaseGrant({ state: GrantState.Approved })
generateBaseGrant({ state: GrantState.Approved, tenantId: tenant.id })
)
grantWithTwoAccesses.access = [
await Access.query(trx).insertAndFetch({
Expand Down Expand Up @@ -247,11 +251,14 @@ describe('Access Token Service', (): void => {
})

describe('Revoke', (): void => {
let tenant: Tenant
let grant: Grant
let token: AccessToken
beforeEach(async (): Promise<void> => {
tenant = await Tenant.query(trx).insertAndFetch(generateTenant())
grant = await Grant.query(trx).insertAndFetch(
generateBaseGrant({
tenantId: tenant.id,
state: GrantState.Finalized,
finalizationReason: GrantFinalization.Issued
})
Expand Down Expand Up @@ -352,8 +359,10 @@ describe('Access Token Service', (): void => {
let token: AccessToken
let originalTokenValue: string
beforeEach(async (): Promise<void> => {
const tenant = await Tenant.query(trx).insertAndFetch(generateTenant())
grant = await Grant.query(trx).insertAndFetch(
generateBaseGrant({
tenantId: tenant.id,
state: GrantState.Finalized,
finalizationReason: GrantFinalization.Issued
})
Expand Down
Loading

0 comments on commit f8a61c4

Please sign in to comment.