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

feat(backend): create operator kratos identity on backend startup #2909

Merged
merged 5 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions localenv/admin-auth/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
services:
cloud-nine-backend:
environment:
KRATOS_ADMIN_URL: http://cloud-nine-kratos:4434/admin
KRATOS_ADMIN_EMAIL: [email protected]
depends_on:
- cloud-nine-kratos
cloud-nine-admin:
environment:
AUTH_ENABLED: true
Expand All @@ -9,6 +15,13 @@ services:
- cloud-nine-backend
- cloud-nine-kratos

happy-life-backend:
environment:
KRATOS_ADMIN_URL: 'http://happy-life-kratos:4434/admin'
KRATOS_ADMIN_EMAIL: '[email protected]'
depends_on:
- happy-life-kratos

happy-life-admin:
environment:
AUTH_ENABLED: true
Expand Down
9 changes: 4 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@
"check:prettier": "prettier --check .",
"clean": "find . -name 'node_modules' -type d -prune -exec rm -rf '{}' +",
"build": "tsc --build",
"localenv:compose:psql": "docker compose -f ./localenv/cloud-nine-wallet/docker-compose.yml -f ./localenv/happy-life-bank/docker-compose.yml -f ./localenv/merged/docker-compose.yml",
"localenv:compose": "docker compose -f ./localenv/cloud-nine-wallet/docker-compose.yml -f ./localenv/happy-life-bank/docker-compose.yml -f ./localenv/merged/docker-compose.yml -f ./localenv/tigerbeetle/docker-compose.yml --env-file ./localenv/tigerbeetle/.env.tigerbeetle",
"localenv:compose:psql:telemetry": "docker compose -f ./localenv/cloud-nine-wallet/docker-compose.yml -f ./localenv/happy-life-bank/docker-compose.yml -f ./localenv/merged/docker-compose.yml -f ./localenv/telemetry/docker-compose.yml",
"localenv:compose:telemetry": "docker compose -f ./localenv/cloud-nine-wallet/docker-compose.yml -f ./localenv/happy-life-bank/docker-compose.yml -f ./localenv/merged/docker-compose.yml -f ./localenv/tigerbeetle/docker-compose.yml -f ./localenv/telemetry/docker-compose.yml --env-file ./localenv/tigerbeetle/.env.tigerbeetle",
"localenv:compose:adminauth": "docker compose -f ./localenv/cloud-nine-wallet/docker-compose.yml -f ./localenv/happy-life-bank/docker-compose.yml -f ./localenv/merged/docker-compose.yml -f ./localenv/admin-auth/docker-compose.yml",
"localenv:compose:psql": "docker compose -f ./localenv/cloud-nine-wallet/docker-compose.yml -f ./localenv/happy-life-bank/docker-compose.yml -f ./localenv/merged/docker-compose.yml -f ./localenv/admin-auth/docker-compose.yml",
"localenv:compose": "docker compose -f ./localenv/cloud-nine-wallet/docker-compose.yml -f ./localenv/happy-life-bank/docker-compose.yml -f ./localenv/merged/docker-compose.yml -f ./localenv/admin-auth/docker-compose.yml -f ./localenv/tigerbeetle/docker-compose.yml --env-file ./localenv/tigerbeetle/.env.tigerbeetle",
"localenv:compose:psql:telemetry": "docker compose -f ./localenv/cloud-nine-wallet/docker-compose.yml -f ./localenv/happy-life-bank/docker-compose.yml -f ./localenv/merged/docker-compose.yml -f ./localenv/admin-auth/docker-compose.yml -f ./localenv/telemetry/docker-compose.yml",
"localenv:compose:telemetry": "docker compose -f ./localenv/cloud-nine-wallet/docker-compose.yml -f ./localenv/happy-life-bank/docker-compose.yml -f ./localenv/merged/docker-compose.yml -f ./localenv/admin-auth/docker-compose.yml -f ./localenv/tigerbeetle/docker-compose.yml -f ./localenv/telemetry/docker-compose.yml --env-file ./localenv/tigerbeetle/.env.tigerbeetle",
"localenv:seed:auth": "pnpm -C ./packages/auth knex seed:run --env=development && pnpm -C ./packages/auth knex seed:run --env=peerdevelopment",
"sanity": "pnpm -r build && pnpm -r test",
"localenv:compose:autopeer": "run-p tunnel:start wait-tunnel:localenv:compose",
Expand Down
2 changes: 2 additions & 0 deletions packages/backend/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ process.env.WEBHOOK_URL = 'http://127.0.0.1:4001/webhook'
process.env.STREAM_SECRET = '2/PxuRFV9PAp0yJlnAifJ+1OxujjjI16lN+DBnLNRLA='
process.env.USE_TIGERBEETLE = false
process.env.ENABLE_TELEMETRY = false
process.env.KRATOS_ADMIN_URL = 'http://127.0.0.1:4434/admin'
process.env.KRATOS_ADMIN_EMAIL = '[email protected]'

module.exports = {
...baseConfig,
Expand Down
91 changes: 91 additions & 0 deletions packages/backend/src/app.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { join } from 'path'
import http, { Server } from 'http'
import { ParsedUrlQuery } from 'querystring'
import axios from 'axios'
import { Client as TigerbeetleClient } from 'tigerbeetle-node'

import { IocContract } from '@adonisjs/fold'
Expand Down Expand Up @@ -297,6 +298,96 @@ export class App {
}
}

public async createOperatorIdentity(): Promise<void> {
const { kratosAdminEmail, kratosAdminUrl } =
await this.container.use('config')
const logger = await this.container.use('logger')
// TODO: error out since kratos is essentially required
if (!kratosAdminUrl || !kratosAdminEmail) {
throw new Error('Missing admin configuration')
}
try {
const identityQueryResponse = await axios.get(
`${kratosAdminUrl}/identities?credentials_identifier=${kratosAdminEmail}`
)
const isExistingIdentity =
identityQueryResponse.data.length > 0 &&
identityQueryResponse.data[0].id
const operatorRole =
identityQueryResponse.data[0]?.metadata_admin.operator
let identityResponse
if (isExistingIdentity && operatorRole) {
// Identity already exists with operator role
logger.debug(
`Identity with email ${kratosAdminEmail} exists on the system with the ID: ${identityQueryResponse.data[0].id}`
)
return
} else if (isExistingIdentity && !operatorRole) {
// Identity already exists but does not have operator role
identityResponse = await axios.put(
`${kratosAdminUrl}/admin/identities/${identityQueryResponse.data[0].id}`,
{
metadata_admin: {
operator: true
}
}
)
logger.debug(
`Successfully created user ${kratosAdminEmail} with ID ${identityResponse.data.id}`
)
} else {
// Identity does not exist
logger.debug(
`No identity with email ${kratosAdminEmail} exists on the system`
)

identityResponse = await axios.post(
`${kratosAdminUrl}/identities`,
{
schema_id: 'default',
traits: {
email: kratosAdminEmail
},
metadata_admin: {
operator: true
}
},
{
headers: {
'Content-Type': 'application/json'
}
}
)
logger.debug(
`Successfully created user ${kratosAdminEmail} with ID ${identityResponse.data.id}`
)
}

const recoveryCodeResponse = await axios.post(
`${kratosAdminUrl}/recovery/link`,
{
identity_id: identityResponse.data.id
}
)
logger.info(
`Recovery link for ${kratosAdminEmail} at ${recoveryCodeResponse.data.recovery_link}`
)
} catch (error) {
if (axios.isAxiosError(error)) {
logger.error(
`Error retrieving identity ${kratosAdminEmail}:`,
error.response?.status,
error.response?.data
)
} else {
logger.error(
`An unexpected error occurred while trying to retrieve the identity for ${kratosAdminEmail}:`,
error
)
}
}
}

public async startAdminServer(port: number): Promise<void> {
const koa = await this.createKoaServer()
const httpServer = http.createServer(koa.callback())
Expand Down
4 changes: 3 additions & 1 deletion packages/backend/src/config/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,9 @@ export const Config = {
'INCOMING_PAYMENT_EXPIRY_MAX_MS',
2592000000
), // 30 days
enableSpspPaymentPointers: envBool('ENABLE_SPSP_PAYMENT_POINTERS', true)
enableSpspPaymentPointers: envBool('ENABLE_SPSP_PAYMENT_POINTERS', true),
kratosAdminUrl: envString('KRATOS_ADMIN_URL'),
kratosAdminEmail: envString('KRATOS_ADMIN_EMAIL')
}

function parseRedisTlsConfig(
Expand Down
2 changes: 2 additions & 0 deletions packages/backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,8 @@ export const start = async (
`Auto-peering server listening on ${config.autoPeeringServerPort}`
)
}

await app.createOperatorIdentity()
}

// If this script is run directly, start the server
Expand Down
7 changes: 7 additions & 0 deletions packages/frontend/kratos/scripts/userInvitation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import { logger } from '../../app/utils/logger.server'

// Use process.argv to accept an email argument from the command line
const USER_EMAIL = process.argv[2]

// Use process.argv to accept privilege argument from the command line
const ROLE = process.argv[3]

if (!USER_EMAIL) {
logger.error('No email argument provided.')
process.exit(1)
Expand Down Expand Up @@ -53,6 +57,9 @@ const createIdentity = async () => {
schema_id: 'default',
traits: {
email: USER_EMAIL
},
metadata_admin: {
[ROLE]: true
}
},
{
Expand Down
2 changes: 2 additions & 0 deletions test/integration/testenv/cloud-nine-wallet/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ services:
EXCHANGE_RATES_URL: http://host.docker.internal:8888/rates
REDIS_URL: redis://shared-redis:6379/0
USE_TIGERBEETLE: false
KRATOS_ADMIN_URL: http://cloud-nine-kratos:4434/admin
KRATOS_ADMIN_EMAIL: [email protected]
volumes:
- ../private-key.pem:/workspace/private-key.pem
depends_on:
Expand Down
2 changes: 2 additions & 0 deletions test/integration/testenv/happy-life-bank/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ services:
EXCHANGE_RATES_URL: http://host.docker.internal:8889/rates
REDIS_URL: redis://shared-redis:6379/2
USE_TIGERBEETLE: false
KRATOS_ADMIN_URL: 'http://happy-life-kratos:4434/admin'
KRATOS_ADMIN_EMAIL: '[email protected]'
volumes:
- ../private-key.pem:/workspace/private-key.pem
depends_on:
Expand Down
Loading