diff --git a/package-lock.json b/package-lock.json index f3a4467..0ef7edd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -87,9 +87,9 @@ } }, "node_modules/@curveball/browser": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@curveball/browser/-/browser-0.19.4.tgz", - "integrity": "sha512-ty1HcA7Z/uYWX/l+FL4UOvCfVwS91tBDB8ZR9jxSg787nsuh9BubWtKbejufGsKetbNzYpbIlV4i34GgC9JYPg==", + "version": "0.19.7", + "resolved": "https://registry.npmjs.org/@curveball/browser/-/browser-0.19.7.tgz", + "integrity": "sha512-H/qd8L60SQW//0svb8Lsfb5UH9FC8lflqxaFW6LdCuizXyN5IKZ9wuJypA7oNWZmyX4cJlPvyJ4HeIn+4XCBzg==", "dependencies": { "@curveball/static": "^0.3.0", "csv-parse": "^5.1.0", @@ -2940,9 +2940,9 @@ "requires": {} }, "@curveball/browser": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@curveball/browser/-/browser-0.19.4.tgz", - "integrity": "sha512-ty1HcA7Z/uYWX/l+FL4UOvCfVwS91tBDB8ZR9jxSg787nsuh9BubWtKbejufGsKetbNzYpbIlV4i34GgC9JYPg==", + "version": "0.19.7", + "resolved": "https://registry.npmjs.org/@curveball/browser/-/browser-0.19.7.tgz", + "integrity": "sha512-H/qd8L60SQW//0svb8Lsfb5UH9FC8lflqxaFW6LdCuizXyN5IKZ9wuJypA7oNWZmyX4cJlPvyJ4HeIn+4XCBzg==", "requires": { "@curveball/static": "^0.3.0", "csv-parse": "^5.1.0", diff --git a/src/a12n.ts b/src/a12n.ts new file mode 100644 index 0000000..5ac8fbc --- /dev/null +++ b/src/a12n.ts @@ -0,0 +1,27 @@ +import ketting from './ketting'; +import { LinkNotFound } from 'ketting'; + + + +export async function addUserPrivilege(principal: string|URL, privilege: string, resource: string|URL): Promise { + let userPrivilegesRes; + + try { + userPrivilegesRes = await ketting.go(principal.toString()).follow('privileges'); + } catch (err) { + if (err instanceof LinkNotFound) { + throw new Error('Link with "privileges" is not found on the user resource. This could mean that the tt-api APP in a12n-server does not have the *admin" privilege'); + } + throw err; + } + + const userPrivilegesState = await userPrivilegesRes.get(); + if (!userPrivilegesState.hasAction('add')) { + throw new Error('The privileges resource on a12nserver does not have an \'add\' action. You likely need to update your a12n-server for this to work'); + } + await userPrivilegesState.action('add').submit({ + action: 'add', + privilege, + resource: resource.toString() + }); +} diff --git a/src/app.ts b/src/app.ts index 339272a..38cf6f6 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,3 +1,6 @@ +import * as dotenv from 'dotenv'; +dotenv.config(); + import accessLog from '@curveball/accesslog'; import bodyParser from '@curveball/bodyparser'; import browser from '@curveball/browser'; @@ -9,15 +12,11 @@ import cors from '@curveball/cors'; import session from '@curveball/session'; import browserToBearer from '@curveball/browser-to-bearer'; import oauth2 from '@curveball/oauth2'; -import { OAuth2Client } from '@badgateway/oauth2-client'; +import oauth2Client from './oauth2'; import * as path from 'path'; -import * as dotenv from 'dotenv'; - import routes from './routes'; -dotenv.config(); - const app = new Application(); // The accesslog middleware shows all requests and responses on the cli. @@ -57,20 +56,16 @@ app.use(validator({ schemaPath: path.join(__dirname, '../node_modules/@badgateway/tt-types/schema') })); -// a12n setup -const client = new OAuth2Client({ - server: process.env.AUTH_API_URI, - clientId: process.env.OAUTH2_CLIENT_ID || 'tt-api', - clientSecret: process.env.OAUTH2_CLIENT_SECRET, -}); -app.use(browserToBearer({client})); +app.use(browserToBearer({ + client: oauth2Client, +})); app.use(oauth2({ publicPrefixes: [ '/health', ], - client, + client: oauth2Client, })); diff --git a/src/client/controller/collection.ts b/src/client/controller/collection.ts index e10502b..9d10f02 100644 --- a/src/client/controller/collection.ts +++ b/src/client/controller/collection.ts @@ -5,6 +5,8 @@ import * as hal from '../formats/hal'; import * as clientService from '../service'; import { ClientNew as ClientNewSchema } from '@badgateway/tt-types'; +import { addUserPrivilege } from '../../a12n'; + class ClientCollection extends Controller { @@ -25,6 +27,12 @@ class ClientCollection extends Controller { name: body.name, }); + await addUserPrivilege( + ctx.state.oauth2._links['authenticated-as'].href, + 'owner', + new URL(client.href, ctx.request.origin), + ); + ctx.status = 201; ctx.response.headers.set('Location', client.href); diff --git a/src/client/service.ts b/src/client/service.ts index f5139e8..c3272a2 100644 --- a/src/client/service.ts +++ b/src/client/service.ts @@ -3,6 +3,7 @@ import { NotFound } from '@curveball/http-errors'; import knex from '../db'; import { ClientsRecord } from 'knex/types/tables'; + export async function findAll(): Promise { return ( @@ -63,3 +64,4 @@ function mapRecord(input: ClientsRecord): Client { }; } + diff --git a/src/ketting.ts b/src/ketting.ts new file mode 100644 index 0000000..d9e864b --- /dev/null +++ b/src/ketting.ts @@ -0,0 +1,17 @@ +import { Client } from 'ketting'; +import oauth2Client from './oauth2'; +import { OAuth2Fetch } from '@badgateway/oauth2-client'; + +console.debug('🔗 Setting up Ketting client'); +const client = new Client(process.env.AUTH_API_URI!); + +const oauth2FetchWrapper = new OAuth2Fetch({ + client: oauth2Client, + getNewToken: () => { + return oauth2Client.clientCredentials(); + } +}); + +client.use(oauth2FetchWrapper.mw()); + +export default client; diff --git a/src/oauth2.ts b/src/oauth2.ts new file mode 100644 index 0000000..244b3fd --- /dev/null +++ b/src/oauth2.ts @@ -0,0 +1,8 @@ +import { OAuth2Client } from '@badgateway/oauth2-client'; + +// a12n setup +export default new OAuth2Client({ + server: process.env.AUTH_API_URI, + clientId: process.env.OAUTH2_CLIENT_ID || 'tt-api', + clientSecret: process.env.OAUTH2_CLIENT_SECRET, +});