Skip to content

Commit

Permalink
Add request examples + move api calls to hooks (#226)
Browse files Browse the repository at this point in the history
  • Loading branch information
Samuel authored Apr 15, 2024
1 parent 3ad37a6 commit 540f60a
Show file tree
Hide file tree
Showing 14 changed files with 357 additions and 127 deletions.
15 changes: 4 additions & 11 deletions apps/devtool/src/app/_hooks/useDataStoreApi.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,11 @@ import { Alg, Curves, KeyTypes, Payload, hash } from '@narval/signature'
import axios from 'axios'
import { useEffect, useState } from 'react'
import { v4 as uuid } from 'uuid'
import { useAccount } from 'wagmi'
import useAccountSignature from './useAccountSignature'

type DataStore = { entity: EntityStore; policy: PolicyStore }

const useDataStoreApi = () => {
const account = useAccount()
const { jwk, signAccountJwt } = useAccountSignature()

const [dataStore, setDataStore] = useState<DataStore>()
Expand All @@ -34,14 +32,9 @@ const useDataStoreApi = () => {
}
}, [dataStore])

useEffect(() => {
if (!account.isConnected) return

createCredential(account.address?.toLowerCase())
}, [account.address])

const getDataStore = async () => {
const { data } = await axios.get<DataStore>('/api/data-store')

setDataStore(data)

return data
Expand All @@ -61,14 +54,14 @@ const useDataStoreApi = () => {
const user: UserEntity = { id: uuid(), role: UserRole.ADMIN }

const publicKey: CredentialEntity = {
id: uuid(),
id: address,
userId: user.id,
key: {
kty: KeyTypes.EC,
crv: Curves.SECP256K1,
alg: Alg.ES256K,
kid: address.toLowerCase(),
addr: address.toLowerCase()
kid: address,
addr: address
}
}

Expand Down
66 changes: 55 additions & 11 deletions apps/devtool/src/app/_hooks/useEngineApi.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { EvaluationRequest, EvaluationResponse } from '@narval/policy-engine-shared'
import { hash } from '@narval/signature'
import axios from 'axios'
import { useState } from 'react'
import { v4 as uuid } from 'uuid'
import { extractErrorMessage } from '../_lib/utils'
import useAccountSignature from './useAccountSignature'
import useStore from './useStore'

Expand All @@ -18,10 +22,11 @@ const useEngineApi = () => {
setEngineClientSigner
} = useStore()

const { jwk } = useAccountSignature()
const { jwk, signAccountJwt } = useAccountSignature()
const [isOnboarded, setIsOnboarded] = useState(false)
const [isSynced, setIsSynced] = useState(false)
const [errors, setErrors] = useState<unknown>()

const [errors, setErrors] = useState<any>()

const onboardClient = async () => {
if (!engineAdminApiKey || !jwk) return
Expand Down Expand Up @@ -57,23 +62,62 @@ const useEngineApi = () => {
setIsOnboarded(true)
setTimeout(() => setIsOnboarded(false), 5000)
} catch (error) {
setErrors(error)
setErrors(extractErrorMessage(error))
}
}

const syncEngine = async () => {
await axios.post(`${engineUrl}/clients/sync`, null, {
headers: {
'x-client-id': engineClientId,
'x-client-secret': engineClientSecret
if (!engineClientId || !engineClientSecret) return

setErrors(undefined)

try {
await axios.post(`${engineUrl}/clients/sync`, null, {
headers: {
'x-client-id': engineClientId,
'x-client-secret': engineClientSecret
}
})

setIsSynced(true)
setTimeout(() => setIsSynced(false), 5000)
} catch (error) {
setErrors(extractErrorMessage(error))
}
}

const evaluateRequest = async (evaluationRequest: EvaluationRequest | undefined) => {
if (!engineClientId || !engineClientSecret || !evaluationRequest) return

setErrors(undefined)

try {
const payload = {
iss: uuid(),
sub: evaluationRequest.request.resourceId,
requestHash: hash(evaluationRequest.request)
}
})

setIsSynced(true)
setTimeout(() => setIsSynced(false), 5000)
const authentication = await signAccountJwt(payload)

const { data: evaluation } = await axios.post<EvaluationResponse>(
`${engineUrl}/evaluations`,
{ ...evaluationRequest, authentication },
{
headers: {
'x-client-id': engineClientId,
'x-client-secret': engineClientSecret
}
}
)

return { evaluation, authentication }
} catch (error) {
setErrors(extractErrorMessage(error))
}
}

return { isOnboarded, isSynced, errors, onboardClient, syncEngine }
return { isOnboarded, isSynced, errors, onboardClient, syncEngine, evaluateRequest }
}

export default useEngineApi
58 changes: 55 additions & 3 deletions apps/devtool/src/app/_hooks/useVaultApi.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import { Request, WalletEntity } from '@narval/policy-engine-shared'
import axios from 'axios'
import { useState } from 'react'
import { extractErrorMessage } from '../_lib/utils'
import useAccountSignature from './useAccountSignature'
import useStore from './useStore'

const useVaultApi = () => {
const {
vaultUrl,
vaultAdminApiKey,
vaultClientId,
vaultClientSecret,
engineClientSigner,
setVaultClientId,
setVaultClientSecret,
setEngineClientSigner
} = useStore()

const { signAccountJwsd } = useAccountSignature()

const [isOnboarded, setIsOnboarded] = useState(false)
const [errors, setErrors] = useState<unknown>()
const [errors, setErrors] = useState<any>()

const onboardClient = async () => {
if (!vaultAdminApiKey) return
Expand All @@ -38,11 +45,56 @@ const useVaultApi = () => {
setIsOnboarded(true)
setTimeout(() => setIsOnboarded(false), 5000)
} catch (error) {
setErrors(error)
setErrors(extractErrorMessage(error))
}
}

const importPrivateKey = async (privateKey: string) => {
if (!vaultClientId || !vaultClientSecret || !privateKey) return

setErrors(undefined)

try {
const { data } = await axios.post<WalletEntity>(
`${vaultUrl}/import/private-key`,
{ privateKey },
{
headers: {
'x-client-id': vaultClientId,
'x-client-secret': vaultClientSecret
}
}
)

return data
} catch (error) {
setErrors(extractErrorMessage(error))
}
}

const signTransaction = async (payload: { request: Request }, accessToken: string) => {
if (!vaultClientId || !vaultClientSecret || !accessToken) return

setErrors(undefined)

try {
const detachedJws = await signAccountJwsd(payload, accessToken)

const { data } = await axios.post(`${vaultUrl}/sign`, payload, {
headers: {
'x-client-id': vaultClientId,
'detached-jws': detachedJws,
authorization: `GNAP ${accessToken}`
}
})

return data.signature
} catch (error) {
setErrors(extractErrorMessage(error))
}
}

return { isOnboarded, errors, onboardClient }
return { isOnboarded, errors, onboardClient, importPrivateKey, signTransaction }
}

export default useVaultApi
7 changes: 7 additions & 0 deletions apps/devtool/src/app/_lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { AxiosError } from 'axios'
import { extendTailwindMerge } from 'tailwind-merge'

export const classNames = (...classes: Array<string | undefined | null>) => {
Expand All @@ -6,3 +7,9 @@ export const classNames = (...classes: Array<string | undefined | null>) => {
}

export const capitalize = (str: string) => str.charAt(0).toUpperCase() + str.toLowerCase().slice(1)

export const extractErrorMessage = (err: unknown) => {
const error = err as AxiosError
const data = error.response?.data as any
return data?.message || error.message
}
83 changes: 75 additions & 8 deletions apps/devtool/src/app/api/data-store/example.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,34 @@
{
"entity": {
"signature": "",
"signature": "eyJhbGciOiJFSVAxOTEiLCJraWQiOiIweDAwMGMwZDE5MTMwOEEzMzYzNTZCRWUzODEzQ0MxN0Y2ODY4OTcyQzQiLCJ0eXAiOiJKV1QifQ.eyJkYXRhIjoiMHgzNTE5MTkzZDVkNjQxYjIzYzdmYTIwYjRhMDE2NGI5YmZkZmI4MmQ2Njk5NjZiZWU1YTFhNmYwNmExMzI4ZTdlIiwiaWF0IjoxNzEyODUxNzg0LCJpc3MiOiJodHRwczovL2RldnRvb2wubmFydmFsLnh5eiIsInN1YiI6IjB4MDAwYzBkMTkxMzA4QTMzNjM1NkJFZTM4MTNDQzE3RjY4Njg5NzJDNCJ9.ReYKAos1x-UjAxfMsZVsE6tA2oURthN5iaLW9i9pr6gO37LVLCrKqdJ0ekouNHdJNQ-eI7VwlKct9esi9A1V_Bs",
"data": {
"addressBook": [],
"credentials": [
{
"id": "0x9D432a09CBf55F22Aa6a2E290acB12D57d29B2Fc",
"userId": "7a09904c-070e-4d00-8fa8-5f1dfafff2a5",
"key": {
"kty": "EC",
"crv": "secp256k1",
"alg": "ES256K",
"kid": "0x9D432a09CBf55F22Aa6a2E290acB12D57d29B2Fc",
"addr": "0x9D432a09CBf55F22Aa6a2E290acB12D57d29B2Fc"
}
},
{
"id": "0x0C151023EDedCC419caB0f49ABaCd2e87a4FF013",
"userId": "db5bf088-05f7-492c-a2a9-0b62696ac9c7",
"key": {
"kty": "EC",
"crv": "secp256k1",
"alg": "ES256K",
"kid": "0x0C151023EDedCC419caB0f49ABaCd2e87a4FF013",
"addr": "0x0C151023EDedCC419caB0f49ABaCd2e87a4FF013"
}
},
{
"id": "0x000c0d191308A336356BEe3813CC17F6868972C4",
"userId": "fe723044-35df-4e99-9739-122a48d4ab96",
"userId": "61e775a9-5f68-41ab-a775-5806845e6e72",
"key": {
"kty": "EC",
"crv": "secp256k1",
Expand All @@ -21,29 +43,37 @@
"userGroups": [],
"userWallets": [
{
"userId": "fe723044-35df-4e99-9739-122a48d4ab96",
"walletId": "eip155:eoa:0x56085a7d2e308ec8ce6fd707cdc5282431d025db"
"userId": "61e775a9-5f68-41ab-a775-5806845e6e72",
"walletId": "eip155:eoa:0x494042504a8148a6d00ab10ed26043f5579ce00f"
}
],
"users": [
{
"id": "fe723044-35df-4e99-9739-122a48d4ab96",
"id": "7a09904c-070e-4d00-8fa8-5f1dfafff2a5",
"role": "admin"
},
{
"id": "db5bf088-05f7-492c-a2a9-0b62696ac9c7",
"role": "admin"
},
{
"id": "61e775a9-5f68-41ab-a775-5806845e6e72",
"role": "admin"
}
],
"walletGroupMembers": [],
"walletGroups": [],
"wallets": [
{
"id": "eip155:eoa:0x56085a7d2e308ec8ce6fd707cdc5282431d025db",
"address": "0x56085a7d2e308ec8ce6fd707cdc5282431d025db",
"id": "eip155:eoa:0x494042504a8148a6d00ab10ed26043f5579ce00f",
"address": "0x494042504a8148a6d00ab10ed26043f5579ce00f",
"accountType": "eoa"
}
]
}
},
"policy": {
"signature": "",
"signature": "eyJhbGciOiJFSVAxOTEiLCJraWQiOiIweDAwMGMwZDE5MTMwOEEzMzYzNTZCRWUzODEzQ0MxN0Y2ODY4OTcyQzQiLCJ0eXAiOiJKV1QifQ.eyJkYXRhIjoiMHhhNDRhNmM5YjQzNmQyMDk5Y2Y4MzFjNTdkYmIyZGRmOTUzZTNhNTZhZWE5Y2E2NWI2Yzg3OTA1MDZkYjNjZjExIiwiaWF0IjoxNzEyODU5NDUyLCJpc3MiOiJodHRwczovL2RldnRvb2wubmFydmFsLnh5eiIsInN1YiI6IjB4MDAwYzBkMTkxMzA4QTMzNjM1NkJFZTM4MTNDQzE3RjY4Njg5NzJDNCJ9.9BCnmnwq3rN9YEquH3H1fTvCh7fo1eOlsSszi7guVnAZE54h3yPhGLwRtNHABVgajMKK_DaQg0hOVgKwVvoI9Rs",
"data": [
{
"id": "a68e8d20-0419-475c-8fcc-b17d4de8c955",
Expand All @@ -60,6 +90,43 @@
{
"criterion": "checkIntentType",
"args": ["transferErc721", "transferErc1155"]
},
{
"criterion": "checkApprovals",
"args": [
{
"approvalCount": 2,
"countPrincipal": false,
"approvalEntityType": "Narval::User",
"entityIds": ["7a09904c-070e-4d00-8fa8-5f1dfafff2a5", "db5bf088-05f7-492c-a2a9-0b62696ac9c7"]
}
]
}
],
"then": "permit"
},
{
"id": "a68e8d20-0519-475c-9fcc-b17d4de8c955",
"description": "Authorized transfers <= 1 MATIC on a 24h sliding window",
"when": [
{
"criterion": "checkAction",
"args": ["signTransaction"]
},
{
"criterion": "checkIntentType",
"args": ["transferNative"]
},
{
"criterion": "checkSpendingLimit",
"args": {
"limit": "1000000000000000000",
"operator": "lte",
"timeWindow": {
"type": "rolling",
"value": 43200
}
}
}
],
"then": "permit"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const DataStoreConfig = () => {
useDataStoreApi()

const [codeEditor, setCodeEditor] = useState<string>()
const [displayCodeEditor, setDisplayCodeEditor] = useState(false)
const [displayCodeEditor, setDisplayCodeEditor] = useState(true)
const [isDialogOpen, setIsDialogOpen] = useState(false)

useEffect(() => {
Expand Down
Loading

0 comments on commit 540f60a

Please sign in to comment.