Skip to content

Commit

Permalink
Merge pull request #120 from nevermined-io/feat/api_key
Browse files Browse the repository at this point in the history
feat: management of NVM API Keys
  • Loading branch information
aaitor authored Sep 16, 2024
2 parents 07ab6ee + 4838007 commit 27131d8
Show file tree
Hide file tree
Showing 10 changed files with 325 additions and 6 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nevermined-io/cli",
"version": "2.2.1",
"version": "2.2.2",
"main": "index.js",
"repository": "[email protected]:nevermined-io/cli.git",
"author": "Nevermined",
Expand All @@ -25,7 +25,7 @@
"ncli": "./dist/src/index.js"
},
"dependencies": {
"@nevermined-io/sdk": "3.0.29",
"@nevermined-io/sdk": "3.0.33",
"log4js": "^6.9.1",
"chalk": "^4.1.2",
"cross-fetch": "~3.1.5",
Expand Down
35 changes: 35 additions & 0 deletions resources/commands.json
Original file line number Diff line number Diff line change
Expand Up @@ -2264,6 +2264,41 @@
"hidden": true,
"description": "The NFT type"
}]
}, {
"name": "create-api-key [name]",
"description": "It creates a new NVM API Key",
"details": "Nevermined API Keys allow to interact with the protocol in a programatic way. This command allows the generation of NVM API Keys. NOTE: This method requires the user to setup the ZERO_PROJECT_ID environment variable.",
"examples": ["ncli app create-api-key 'My Test API Key'"],
"commandHandler": "createApiKey",
"requiresAccount": true,
"positionalArguments": [{
"name": "name",
"type": "string",
"description": "The name of the NVM API Key to create"
}],
"optionalArguments": []
}, {
"name": "list-api-keys",
"description": "It lists all the NVM API Keys associated to the user",
"details": "This command lists allt he NVM API Keys created by the user.",
"examples": ["ncli app list-api-keys"],
"commandHandler": "listApiKeys",
"requiresAccount": true,
"positionalArguments": [],
"optionalArguments": []
}, {
"name": "revoke-api-key [hash]",
"description": "It revokes a existing NVM API Key",
"details": "Nevermined API Keys allow to interact with the protocol in a programatic way. This command allows to revoke an existing NVM API Keys.",
"examples": ["ncli app revoke-api-key 'eyJhbGciOiJFUzI1NksifQ.eyJpc'"],
"commandHandler": "revokeApiKey",
"requiresAccount": true,
"positionalArguments": [{
"name": "hash",
"type": "string",
"description": "The hash of the NVM API Key to revoke"
}],
"optionalArguments": []
}]
}
]
Expand Down
14 changes: 12 additions & 2 deletions resources/networks.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"graphHttpUri": "http://localhost:9000/subgraphs/name/nevermined-io/development",
"neverminedNodeUri": "http://node.nevermined.localnet",
"neverminedNodeAddress": "0x068ed00cf0441e4829d9784fcbe7b9e26d4bd8d0",
"neverminedBackendUri": "http://one-backend.nevermined.localnet",
"verbose": true
},
"nativeToken": "ETH",
Expand All @@ -34,6 +35,7 @@
"graphHttpUri": "",
"neverminedNodeUri": "https://node.staging.nevermined.app",
"neverminedNodeAddress": "0x5838B5512cF9f12FE9f2beccB20eb47211F9B0bc",
"neverminedBackendUri": "https://one-backend.staging.nevermined.app",
"verbose": true
},
"nativeToken": "ETH",
Expand All @@ -59,6 +61,7 @@
"graphHttpUri": "https://api.thegraph.com/subgraphs/name/nevermined-io/public",
"neverminedNodeUri": "https://node.matic.nevermined.app",
"neverminedNodeAddress": "0x824dbcE5E9C96C5b8ce2A35a25a5ab87eD1D00b1",
"neverminedBackendUri": "https://one-backend.matic.nevermined.app",
"verbose": true
},
"nativeToken": "MATIC",
Expand All @@ -84,6 +87,7 @@
"graphHttpUri": "",
"neverminedNodeUri": "https://node.testing.nevermined.app",
"neverminedNodeAddress": "0x5838B5512cF9f12FE9f2beccB20eb47211F9B0bc",
"neverminedBackendUri": "https://one-backend.testing.nevermined.app",
"verbose": true
},
"nativeToken": "ETH",
Expand All @@ -109,6 +113,7 @@
"graphHttpUri": "https://api.thegraph.com/subgraphs/name/nevermined-io/public",
"neverminedNodeUri": "https://node.arbitrum.nevermined.app",
"neverminedNodeAddress": "0x824dbcE5E9C96C5b8ce2A35a25a5ab87eD1D00b1",
"neverminedBackendUri": "https://one-backend.arbitrum.nevermined.app",
"verbose": true
},
"nativeToken": "ETH",
Expand All @@ -134,6 +139,7 @@
"graphHttpUri": "https://api.thegraph.com/subgraphs/name/nevermined-io/public",
"neverminedNodeUri": "https://node.base.nevermined.app",
"neverminedNodeAddress": "0x824dbcE5E9C96C5b8ce2A35a25a5ab87eD1D00b1",
"neverminedBackendUri": "https://one-backend.base.nevermined.app",
"verbose": true
},
"nativeToken": "ETH",
Expand All @@ -159,6 +165,7 @@
"graphHttpUri": "https://api.thegraph.com/subgraphs/name/nevermined-io/public",
"neverminedNodeUri": "https://node.gnosis.nevermined.app",
"neverminedNodeAddress": "0x824dbcE5E9C96C5b8ce2A35a25a5ab87eD1D00b1",
"neverminedBackendUri": "https://one-backend.gnosis.nevermined.app",
"verbose": true
},
"nativeToken": "xDAI",
Expand All @@ -184,6 +191,7 @@
"graphHttpUri": "https://api.thegraph.com/subgraphs/name/nevermined-io/public",
"neverminedNodeUri": "https://node.optimism.nevermined.app",
"neverminedNodeAddress": "0x824dbcE5E9C96C5b8ce2A35a25a5ab87eD1D00b1",
"neverminedBackendUri": "https://one-backend.optimism.nevermined.app",
"verbose": true
},
"nativeToken": "ETH",
Expand All @@ -209,6 +217,7 @@
"graphHttpUri": "",
"neverminedNodeUri": "http://node.celo.nevermined.app",
"neverminedNodeAddress": "0x824dbcE5E9C96C5b8ce2A35a25a5ab87eD1D00b1",
"neverminedBackendUri": "https://one-backend.celo.nevermined.app",
"verbose": true
},
"nativeToken": "CELO",
Expand All @@ -232,13 +241,14 @@
"web3ProviderUri": "https://evm.peaq.network",
"marketplaceUri": "https://marketplace-api.peaq.nevermined.app",
"graphHttpUri": "",
"neverminedNodeUri": "http://node.peaq.nevermined.app",
"neverminedNodeUri": "https://node.peaq.nevermined.app",
"neverminedNodeAddress": "0x824dbcE5E9C96C5b8ce2A35a25a5ab87eD1D00b1",
"neverminedBackendUri": "https://one-backend.peaq.nevermined.app",
"verbose": true
},
"nativeToken": "PEAQ",
"networkName": "peaq-mainnet",
"contractsVersion": "3.5.7",
"contractsVersion": "3.5.8",
"tagName": "public",
"etherscanUrl": "https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fmpfn1.peaq.network#/explorer",
"erc20TokenAddress": "0x0000000000000000000000000000000000000000",
Expand Down
133 changes: 133 additions & 0 deletions src/commands/app/createApiKey.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { NvmAccount, NvmApp, getFullZeroDevPermissions, createSessionKey, NvmApiKey } from '@nevermined-io/sdk'
import {
StatusCodes,
getNeverminedContractAbi,
} from '../../utils'
import chalk from 'chalk'
import { ExecutionOutput } from '../../models/ExecutionOutput'
import { Logger } from 'log4js'
import { ConfigEntry } from '../../models/ConfigDefinition'
import { createPublicClient, http, toHex } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'


export const createApiKey = async (
nvmApp: NvmApp,
publisherAccount: NvmAccount,
argv: any,
config: ConfigEntry,
logger: Logger
): Promise<ExecutionOutput> => {

const ZERO_PROJECT_ID = process.env.ZERO_PROJECT_ID || undefined
if (!ZERO_PROJECT_ID) {
return {
status: StatusCodes.ERROR,
errorMessage: `Missing environment variable ZERO_PROJECT_ID`
}
}

const NVM_BACKEND_URL = config.nvm.neverminedBackendUri || undefined
if (!NVM_BACKEND_URL) {
return {
status: StatusCodes.ERROR,
errorMessage: `Missing Nevermined Backend URL`
}
}

const BUNDLER_RPC = `https://rpc.zerodev.app/api/v2/bundler/${ZERO_PROJECT_ID}`

const publicClient = createPublicClient({
chain: nvmApp.sdk.client.chain,
transport: http(BUNDLER_RPC),
})
logger.debug(`Public client created with chain: ${nvmApp.sdk.client.chain?.name}`)
logger.debug(BUNDLER_RPC)

logger.info(chalk.dim(`Creating a new NVM API Key ...`))


const networkName = config.networkName || 'geth-localnet'
const didRegistryAbi = getNeverminedContractAbi('DIDRegistry', networkName)
const nftSalesTemplateAbi = getNeverminedContractAbi('NFTSalesTemplate', networkName)
const nftPlansAbi = getNeverminedContractAbi('NFT1155SubscriptionUpgradeable', networkName)

logger.debug(`Generating permissions for the API Key ...`)
const permissions = getFullZeroDevPermissions(
didRegistryAbi.address, // DIDRegistry address
nftSalesTemplateAbi.address, // Sales Template address
config.erc20TokenAddress as `0x${string}`, // ERC20 address
nftPlansAbi.address, // NFT1155 address
nftPlansAbi.address,
)

logger.debug(`Permissions generated for network: ${networkName}`)
logger.debug(`DIDRegistry: ${didRegistryAbi.address}`)
logger.debug(`NFTSalesTemplate: ${nftSalesTemplateAbi.address}`)
logger.debug(`NFTPlansAbi: ${nftPlansAbi.address}`)
logger.debug(`ERC20 Address: ${config.erc20TokenAddress}`)
// logger.debug(JSON.stringify(permissions))


logger.debug(`Creating a new ZeroDev kernel client ...`)
logger.debug(`Chain ID: ${nvmApp.sdk.client.chain?.id}`)

const privateKey = toHex(publisherAccount.getAccountSigner().getHdKey().privateKey)

const account = privateKeyToAccount(privateKey)
logger.debug(`Private key Account: ${account.address}`)

logger.debug(`Creating a new ZeroDev session key via account: ${account.address} ...`)
const sessionKey = await createSessionKey(account, publicClient, permissions)

logger.debug(`Generating encrypted NVM API Key ...`)
const encryptedNvmApiKey = await NvmApiKey.generate(
nvmApp.sdk.utils.signature,
publisherAccount,
sessionKey,
config.nvm.marketplaceAuthToken!,
config.nvm.neverminedNodeAddress!,
await nvmApp.sdk.services.node.getEcdsaPublicKey()
)
// logger.debug(encryptedNvmApiKey)

logger.debug(`Registering NVM API Key ...`)
const options = {
method: 'POST',
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify({
name: argv.name,
nvmKey: encryptedNvmApiKey
})
}

// logger.debug(`POST ${NVM_BACKEND_URL} with options`)
// logger.debug(JSON.parse(options.body))
const _result = await fetch(`${NVM_BACKEND_URL}/api/v1/api-keys`, options)
if (_result.status !== 200 && _result.status !== 201) {
logger.error(`Error registering NVM API Key: ${_result.statusText}`)
logger.error(await _result.text())
return {
status: StatusCodes.ERROR,
errorMessage: `Error registering NVM API Key: ${_result.statusText}`
}
}

logger.info(chalk.green(`NVM API Key created successfully`))
const keyResult = await _result.json()
logger.info(chalk.dim(`API Key Wallet: ${chalk.yellow(keyResult.userWallet)}`))
logger.info(chalk.dim(`API Key UserId: ${chalk.yellow(keyResult.userId)}`))
logger.info(chalk.dim(`API Key Hash: ${chalk.yellow(keyResult.hash)}`))

return {
status: StatusCodes.OK,
results: JSON.stringify({
sessionKey,
userWallet: keyResult.userWallet,
userId: keyResult.userId,
hash: keyResult.hash
})
}
}
77 changes: 77 additions & 0 deletions src/commands/app/listApiKeys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { NvmAccount, NvmApp } from '@nevermined-io/sdk'
import {
StatusCodes,
} from '../../utils'
import chalk from 'chalk'
import { ExecutionOutput } from '../../models/ExecutionOutput'
import { Logger } from 'log4js'
import { ConfigEntry } from '../../models/ConfigDefinition'


export const listApiKeys = async (
_nvmApp: NvmApp,
_publisherAccount: NvmAccount,
_argv: any,
config: ConfigEntry,
logger: Logger
): Promise<ExecutionOutput> => {

const NVM_BACKEND_URL = config.nvm.neverminedBackendUri || undefined
if (!NVM_BACKEND_URL) {
return {
status: StatusCodes.ERROR,
errorMessage: `Missing Nevermined Backend URL`
}
}

logger.info(chalk.dim(`Listing NVM API Keys ...`))
// logger.info(config.nvm.marketplaceAuthToken)
// logger.info(config.nvm.marketplaceUri)
// const clientAssertion = await nvmApp.sdk.utils.jwt.generateClientAssertion(publisherAccount)
// const marketplaceAuthToken = await nvmApp.sdk.services.marketplace.login(clientAssertion)

const options = {
method: 'GET',
headers: {
'Accept': '*/*',
'Authorization': `Bearer ${config.nvm.marketplaceAuthToken}`
}
}

logger.debug(`${NVM_BACKEND_URL}/api/v1/api-keys`)
logger.debug(JSON.stringify(options))
const _result = await fetch(`${NVM_BACKEND_URL}/api/v1/api-keys`, options)

if (_result.status !== 200) {
logger.error(`Error listing NVM API Keys: ${_result.statusText}`)
logger.error(await _result.text())
return {
status: StatusCodes.ERROR,
errorMessage: `Error listing NVM API Keys: ${_result.statusText}`
}
}

const keyResult = await _result.json()
logger.info(chalk.green(`NVM API Keys found ${keyResult.totalResults}`))


keyResult.apiKeys.forEach((key: any) => {
logger.info('---------------------------------')
logger.info(chalk.dim(`\tName: ${chalk.yellow(key.name)}`))
if (key.isActive)
logger.info(chalk.dim(`\tIs Active?: ${chalk.green('yes')}`))
else
logger.info(chalk.dim(`\tIs Active?: ${chalk.red('no')}`))

logger.info(chalk.dim(`\tHash: ${chalk.yellow(key.hash)}`))
logger.info(chalk.dim(`\tWallet: ${chalk.yellow(key.userWallet)}`))
logger.info(chalk.dim(`\tCreated/Expires: ${chalk.yellow(key.createdAt)} / ${chalk.yellow(key.expiresAt)}`))
})

return {
status: StatusCodes.OK,
results: JSON.stringify({
keyResult
})
}
}
Loading

0 comments on commit 27131d8

Please sign in to comment.