Skip to content

Commit

Permalink
Merge branch 'main' into 1585-prevent-brute-force-attacks-support-rat…
Browse files Browse the repository at this point in the history
…e-limiting-login-attempts
  • Loading branch information
adrianboros committed Oct 3, 2024
2 parents 4addec6 + 314bc80 commit d53cc58
Show file tree
Hide file tree
Showing 65 changed files with 2,323 additions and 1,402 deletions.
13 changes: 3 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Please read the [contribution guidelines](.github/contributing.md) before submit

- [Docker](https://docs.docker.com/get-docker/)
- [NVM](https://github.com/nvm-sh/nvm)
- [Rapyd](https://www.rapyd.net) account in Sandbox mode
- [GateHub](https://sandbox.gatehub.net) account in Sandbox mode

### Environment Setup

Expand Down Expand Up @@ -86,7 +86,7 @@ cp ./docker/dev/.env.example ./docker/dev/.env
```

Using your preferred text editor, open the `./docker/dev/.env` file and configure the necessary environment variables.
The `RAPYD_ACCESS_KEY` and `RAPYD_SECRET_KEY` variables values can be found in your Rapyd Sandbox account (you need to create an account at [rapyd.net](https://www.rapyd.net)), under the Developers menu item. The `RAPYD_SETTLEMENT_EWALLET` variable value can be found in your Rapyd Sandbox account details.
The `GATEHUB` related environment variables are necessary in order to complete Sandbox KYC, and add play money to your account. In order to have the correct variables, create a `GateHub` Sandbox account. Optionally you could send an email to `timea@interledger.foundation` and request these variables.

To create a new Interledger Test Wallet account, a verification email will be sent to the provided email address. If you want to send emails within the development environment, you will need to have a personal Sendgrid account and update the following environment variables: `SEND_EMAIL` to `true`, `SENDGRID_API_KEY` and `FROM_EMAIL`. If you prefer not to send emails in the development environment, simply set `SEND_EMAIL` to `false` and use the verification link found in the Docker `wallet-backend` container logs to finalize the registration process for a new user.

Expand All @@ -95,14 +95,7 @@ To enable rate limiter on the wallet for security purposes you can set these env
Cross-currency transactions are supported. To enable this functionality, you will need to register at [freecurrencyapi.com/](https://freecurrencyapi.com/) and update the `RATE_API_KEY` environment variable with your own API key.
Currencies can be added in the `admin` environment. For example `assetCode` is `EUR`, `assetScale` is `2`, and you will need to add an amount to `liquidity`.

To have everything ready for `DEV` environment, we already set up some default values for Interledger Test Wallet, this way developers are ready to login without validation, and test e-commerce application without any additional setup:

- a `USD` asset set by default in the `admin` environment
- a user with email address `dev@email.com` and password `123456`, with a `USD` account, payment pointer and test money
- a user with email address `boutique@email.com` and password `123456`, with a `USD` account and a payment pointer `boutique`, which is used as a receiver payment pointer at the e-commerce application
- developer keys for the `boutique` payment pointer, these values will be copied to `.env` file from `.env.example`, as mentioned above

If you would like to set up e-commerce application manually for another payment pointer, you will need to create a USD payment pointer, then generate public and private key for the payment pointer in the `Developer Keys` found in the `Settings` menu of Interledger Test Wallet. You also need to update the following environment variables: `PRIVATE_KEY` to the generated base64 encoded private key, `KEY_ID` to the payment pointer key id and `PAYMENT_POINTER` to the created payment pointer address.
If you would like to set up e-commerce application, you will need to create a USD payment pointer, then generate public and private key for the payment pointer in the `Developer Keys` found in the `Settings` menu of Interledger Test Wallet. You also need to update the following environment variables: `PRIVATE_KEY` to the generated base64 encoded private key, `KEY_ID` to the payment pointer key id and `PAYMENT_POINTER` to the created payment pointer address.

### Local Playground

Expand Down
7 changes: 4 additions & 3 deletions docker/dev/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ GATEHUB_GATEWAY_UUID=
GATEHUB_VAULT_UUID_EUR=
GATEHUB_VAULT_UUID_USD=
GATEHUB_SETTLEMENT_WALLET_ADDRESS=
GATEHUB_CARD_APP_ID=
RATE_LIMIT=
RATE_LIMIT_LEVEL=

# commerce env variables
# encoded base 64 private key
PRIVATE_KEY=LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1DNENBUUF3QlFZREsyVndCQ0lFSUI0YzgyOVlSMDZCTUhtQmpIVTNrOHZmMEZhVEFvOHNCTzIrRFZwY1lPSk0KLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQ==
KEY_ID=32499ede-78d9-424d-a43e-3796ee07b60c
PAYMENT_POINTER=https://rafiki-backend/boutique
PRIVATE_KEY=
KEY_ID=
PAYMENT_POINTER=
4 changes: 3 additions & 1 deletion docker/dev/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ services:
GATEHUB_VAULT_UUID_EUR: ${GATEHUB_VAULT_UUID_EUR}
GATEHUB_VAULT_UUID_USD: ${GATEHUB_VAULT_UUID_USD}
GATEHUB_SETTLEMENT_WALLET_ADDRESS: ${GATEHUB_SETTLEMENT_WALLET_ADDRESS}
GATEHUB_CARD_APP_ID: ${GATEHUB_CARD_APP_ID}
RATE_LIMIT: ${RATE_LIMIT}
RATE_LIMIT_LEVEL: ${RATE_LIMIT_LEVEL}
restart: always
Expand Down Expand Up @@ -180,6 +181,7 @@ services:
KRATOS_CONTAINER_PUBLIC_URL: 'http://kratos:4433'
KRATOS_BROWSER_PUBLIC_URL: 'http://localhost:4433'
KRATOS_ADMIN_URL: 'http://kratos:4434/admin'
AUTH_ENABLED: false
<<: *logging

kratos:
Expand All @@ -199,7 +201,7 @@ services:
- testnet

tigerbeetle:
image: ghcr.io/tigerbeetle/tigerbeetle:0.16.2
image: ghcr.io/tigerbeetle/tigerbeetle:0.16.3
privileged: true
volumes:
- tigerbeetle-data:/var/lib/tigerbeetle
Expand Down
1 change: 1 addition & 0 deletions docker/prod/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ WALLET_BACKEND_GATEHUB_GATEWAY_UUID=
WALLET_BACKEND_GATEHUB_VAULT_UUID_EUR=
WALLET_BACKEND_GATEHUB_VAULT_UUID_USD=
WALLET_BACKEND_GATEHUB_SETTLEMENT_WALLET_ADDRESS=
WALLET_BACKEND_GATEHUB_CARD_APP_ID=

# BOUTIQUE
BOUTIQUE_BACKEND_PORT=
Expand Down
4 changes: 2 additions & 2 deletions docker/prod/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ services:
args:
PORT: ${WALLET_FRONTEND_PORT}
COOKIE_NAME: ${WALLET_BACKEND_COOKIE_NAME}
NEXT_PUBLIC_USE_TEST_KYC_DATA: ${WALLET_FRONTEND_USE_TEST_KYC_DATA}
NEXT_PUBLIC_BACKEND_URL: ${WALLET_FRONTEND_BACKEND_URL}
NEXT_PUBLIC_OPEN_PAYMENTS_HOST: ${WALLET_FRONTEND_OPEN_PAYMENTS_HOST}
NEXT_PUBLIC_AUTH_HOST: ${WALLET_FRONTEND_AUTH_HOST}
Expand Down Expand Up @@ -67,6 +66,7 @@ services:
GATEHUB_VAULT_UUID_EUR: ${WALLET_BACKEND_GATEHUB_VAULT_UUID_EUR}
GATEHUB_VAULT_UUID_USD: ${WALLET_BACKEND_GATEHUB_VAULT_UUID_USD}
GATEHUB_SETTLEMENT_WALLET_ADDRESS: ${WALLET_BACKEND_GATEHUB_SETTLEMENT_WALLET_ADDRESS}
GATEHUB_CARD_APP_ID: ${WALLET_BACKEND_GATEHUB_CARD_APP_ID}
networks:
- testnet
ports:
Expand Down Expand Up @@ -232,7 +232,7 @@ services:
<<: *logging

tigerbeetle:
image: ghcr.io/tigerbeetle/tigerbeetle:0.16.2
image: ghcr.io/tigerbeetle/tigerbeetle:0.16.3
privileged: true
volumes:
- tigerbeetle-data:/var/lib/tigerbeetle
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"eslint-plugin-react-hooks": "^4.6.2",
"only-allow": "^1.2.1",
"prettier": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.6",
"prettier-plugin-tailwindcss": "^0.6.8",
"typescript": "^5.6.2"
},
"engines": {
Expand Down
2 changes: 1 addition & 1 deletion packages/boutique/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"express": "^4.21.0",
"helmet": "^7.1.0",
"knex": "^3.1.0",
"objection": "^3.1.4",
"objection": "^3.1.5",
"pg": "^8.13.0",
"winston": "^3.14.2",
"zod": "^3.23.8"
Expand Down
6 changes: 3 additions & 3 deletions packages/boutique/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@
"devDependencies": {
"@tailwindcss/forms": "^0.5.9",
"@tailwindcss/typography": "^0.5.15",
"@types/react": "18.3.8",
"@types/react": "18.3.10",
"@types/react-dom": "18.3.0",
"@vitejs/plugin-react-swc": "^3.7.0",
"autoprefixer": "^10.4.20",
"postcss": "^8.4.47",
"tailwindcss": "^3.4.12",
"tailwindcss": "^3.4.13",
"typescript": "^5.6.2",
"vite": "^5.4.7"
"vite": "^5.4.8"
}
}
2 changes: 1 addition & 1 deletion packages/shared/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"@google-cloud/logging-winston": "^6.0.0",
"awilix": "^11.0.0",
"express": "^4.21.0",
"objection": "^3.1.4",
"objection": "^3.1.5",
"knex": "^3.1.0",
"winston": "^3.14.2",
"axios": "^1.7.7"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ exports.up = function (knex) {
table.string('firstName')
table.string('address')
table.string('country')
table.string('kycId') // TODO: replace with GateHub kyc check

table.boolean('kycVerified').defaultTo(false)
table.string('gateHubUserId')

table.timestamp('createdAt').notNullable()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
exports.up = function (knex) {
return knex.schema.table('walletAddresses', function (table) {
table.string('cardId').unique().nullable()
})
}

exports.down = function (knex) {
return knex.schema.table('walletAddresses', function (table) {
table.dropColumn('cardId')
})
}
4 changes: 2 additions & 2 deletions packages/wallet/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"knex": "^3.1.0",
"moment": "^2.30.1",
"node-cache": "^5.1.2",
"objection": "^3.1.4",
"objection": "^3.1.5",
"pg": "^8.13.0",
"randexp": "^0.5.3",
"rate-limiter-flexible": "^5.0.3",
Expand All @@ -39,7 +39,7 @@
"zod": "^3.23.8"
},
"devDependencies": {
"@faker-js/faker": "^9.0.1",
"@faker-js/faker": "^9.0.3",
"@graphql-codegen/cli": "^5.0.2",
"@graphql-codegen/typescript": "^4.0.9",
"@graphql-codegen/typescript-operations": "^4.2.3",
Expand Down
29 changes: 12 additions & 17 deletions packages/wallet/backend/src/account/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ interface IAccountService {
includeWalletKeys?: boolean
) => Promise<Account[]>
getAccountById: (userId: string, accountId: string) => Promise<Account>
getAccountBalance: (userId: string, account: Account) => Promise<number>
getAccountBalance: (account: Account) => Promise<number>
}

export class AccountService implements IAccountService {
Expand Down Expand Up @@ -109,10 +109,12 @@ export class AccountService implements IAccountService {
const accounts = await query

if (!includeWalletAddress) {
accounts.forEach(async (acc) => {
const balance = await this.getAccountBalance(userId, acc)
acc.balance = transformBalance(balance, acc.assetScale)
})
await Promise.all(
accounts.map(async (acc) => {
const balance = await this.getAccountBalance(acc)
acc.balance = transformBalance(balance, acc.assetScale)
})
)
}

return accounts
Expand All @@ -132,7 +134,7 @@ export class AccountService implements IAccountService {
}

account.balance = transformBalance(
await this.getAccountBalance(userId, account),
await this.getAccountBalance(account),
account.assetScale
)

Expand All @@ -152,26 +154,19 @@ export class AccountService implements IAccountService {
}

account.balance = transformBalance(
await this.getAccountBalance(userId, account),
await this.getAccountBalance(account),
account.assetScale
)

return account
}

async getAccountBalance(userId: string, account: Account): Promise<number> {
const user = await User.query().findById(userId)

if (!user || !user.gateHubUserId) {
throw new NotFound()
}

async getAccountBalance(account: Account): Promise<number> {
const balances = await this.gateHubClient.getWalletBalance(
account.gateHubWalletId,
userId
account.gateHubWalletId
)
return Number(
balances.find((balance) => balance.vault.assetCode === account.assetCode)
balances.find((balance) => balance.vault.asset_code === account.assetCode)
?.total ?? 0
)
}
Expand Down
33 changes: 33 additions & 0 deletions packages/wallet/backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ import { initErrorHandler, RedisClient } from '@shared/backend'
import { GateHubController } from '@/gatehub/controller'
import { GateHubClient } from '@/gatehub/client'
import { GateHubService } from '@/gatehub/service'
import { CardController } from './card/controller'
import { CardService } from './card/service'

export interface Bindings {
env: Env
Expand Down Expand Up @@ -78,6 +80,8 @@ export interface Bindings {
gateHubClient: GateHubClient
gateHubController: GateHubController
gateHubService: GateHubService
cardService: CardService
cardController: CardController
}

export class App {
Expand Down Expand Up @@ -146,6 +150,7 @@ export class App {
const accountController = await this.container.resolve('accountController')
const rafikiController = await this.container.resolve('rafikiController')
const gateHubController = await this.container.resolve('gateHubController')
const cardController = await this.container.resolve('cardController')

app.use(
cors({
Expand Down Expand Up @@ -303,6 +308,34 @@ export class App {

// GateHub
router.get('/iframe-urls/:type', isAuth, gateHubController.getIframeUrl)
router.post('/gatehub-webhooks', gateHubController.webhook)
router.post(
'/gatehub/add-user-to-gateway',
isAuth,
gateHubController.addUserToGateway
)

// Cards
router.get(
'/customers/:customerId/cards',
isAuth,
cardController.getCardsByCustomer
)
router.get('/cards/:cardId/details', isAuth, cardController.getCardDetails)
router.put('/cards/:cardId/lock', isAuth, cardController.lock)
router.put('/cards/:cardId/unlock', isAuth, cardController.unlock)
router.get(
'/cards/:cardId/transactions',
isAuth,
cardController.getCardTransactions
)
router.get('/cards/:cardId/pin', isAuth, cardController.getPin)
router.post('/cards/:cardId/change-pin', isAuth, cardController.changePin)
router.put(
'/cards/:cardId/block',
isAuth,
cardController.permanentlyBlockCard
)

// Return an error for invalid routes
router.use('*', (req: Request, res: CustomResponse) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/wallet/backend/src/auth/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export class AuthController implements IAuthController {
id: user.id,
email: user.email,
needsWallet: !user.gateHubUserId,
needsIDProof: !user.kycId
needsIDProof: !user.kycVerified
}

await req.session.save()
Expand Down
Loading

0 comments on commit d53cc58

Please sign in to comment.