Skip to content
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
29 changes: 8 additions & 21 deletions docs-main/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,7 @@
},
{
"group": "Core Concepts",
"pages": [
"overview/understand/core-concepts"
]
"pages": ["overview/understand/core-concepts"]
},
{
"group": "Global Synchronizer",
Expand Down Expand Up @@ -303,9 +301,7 @@
},
{
"group": "App Rewards",
"pages": [
"appdev/app-rewards"
]
"pages": ["appdev/app-rewards"]
},
{
"group": "Troubleshooting",
Expand Down Expand Up @@ -728,9 +724,7 @@
"groups": [
{
"group": "Overview",
"pages": [
"sdks-tools/overview"
]
"pages": ["sdks-tools/overview"]
},
{
"group": "SDKs",
Expand All @@ -745,7 +739,6 @@
"group": "Using the SDK",
"pages": [
"sdks-tools/sdks/wallet-sdk/using-the-sdk/configuration",
"sdks-tools/sdks/wallet-sdk/using-the-sdk/user-management",
"sdks-tools/sdks/wallet-sdk/using-the-sdk/registering-plugins",
{
"group": "v0 to v1 migration",
Expand All @@ -770,7 +763,8 @@
"sdks-tools/sdks/wallet-sdk/guides/preparing-and-signing-a-transaction",
"sdks-tools/sdks/wallet-sdk/guides/transfer-types",
"sdks-tools/sdks/wallet-sdk/guides/performing-a-cc-tap",
"sdks-tools/sdks/wallet-sdk/guides/signing-transactions-from-third-party-dapps"
"sdks-tools/sdks/wallet-sdk/guides/signing-transactions-from-third-party-dapps",
"sdks-tools/sdks/wallet-sdk/guides/user-management"
]
},
"integrations/release-notes/wallet-sdk"
Expand Down Expand Up @@ -2178,9 +2172,7 @@
"source": "openapi/splice/scan/scan-stream-server.yaml",
"directory": "reference/splice-scan-streaming-api"
},
"pages": [
"GET /v0/history/bulk/download/{object_key}"
]
"pages": ["GET /v0/history/bulk/download/{object_key}"]
}
]
},
Expand All @@ -2207,10 +2199,7 @@
"source": "openapi/splice/validator/ans-external.yaml",
"directory": "reference/splice-ans-api"
},
"pages": [
"POST /v0/entry/create",
"GET /v0/entry/all"
]
"pages": ["POST /v0/entry/create", "GET /v0/entry/all"]
},
{
"group": "Scan Proxy API",
Expand Down Expand Up @@ -2845,9 +2834,7 @@
"product": "Version Dashboard",
"icon": "table",
"root": "shared/version-compatibility-dashboard",
"pages": [
"shared/version-compatibility-dashboard"
]
"pages": ["shared/version-compatibility-dashboard"]
}
]
},
Expand Down
202 changes: 202 additions & 0 deletions docs-main/sdks-tools/sdks/wallet-sdk/guides/user-management.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
---
title: "User Management"
description: "Create users and configure ledger read/act rights, including canReadAsAnyParty and canExecuteAsAnyParty."
---

The Wallet SDK has functionality for creating and managing user rights, by default when you are connecting it uses whichever user is defined in your token provider config. If the user is an admin user on the ledger api they can be used to create other users and grant them rights.

## How do I quickly setup canReadAsAnyParty and canExecuteAsAnyParty?

This script sets up three users `alice`, `bob` and `master`. `master` is given canReadAsAnyParty and canExecuteAsAnyParty and it shows proper access control by creating parties and ensuring that `alice` and `bob` can not see each others parties.

```typescript
import { localNetStaticConfig, SDK } from "@canton-network/wallet-sdk";
import { pino } from "pino";
import { TOKEN_PROVIDER_CONFIG_DEFAULT } from "./utils/index.js";
const logger = pino({ name: "v1-multi-user-setup", level: "info" });

logger.info("Operator sets up users and primary parties");

const operatorSdk = await SDK.create({
auth: TOKEN_PROVIDER_CONFIG_DEFAULT,
ledgerClientUrl: localNetStaticConfig.LOCALNET_APP_USER_LEDGER_URL,
});

const aliceInternal = await operatorSdk.party.internal.allocate({
partyHint: "v1-09-alice",
});

const bobInternal = await operatorSdk.party.internal.allocate({
partyHint: "v1-09-bob",
});

const masterPartyInternal = await operatorSdk.party.internal.allocate({
partyHint: "v1-09-master",
});

logger.info("Created the internal parties");

const aliceUser = await operatorSdk.user.create({
userId: "alice-user",
primaryParty: aliceInternal,
userRights: {
participantAdmin: true,
},
});

const bobUser = await operatorSdk.user.create({
userId: "bob-user",
primaryParty: bobInternal,
userRights: {
participantAdmin: true,
},
});

const masterUser = await operatorSdk.user.create({
userId: "master-user",
primaryParty: masterPartyInternal,
userRights: {
participantAdmin: true,
},
});

logger.info("created the users");

if (!(aliceUser || bobUser || masterUser)) {
throw new Error(`One of the users was not created correctly`);
}

await operatorSdk.user.rights.grant({
userId: masterUser.id!,
userRights: {
canExecuteAsAnyParty: true,
canReadAsAnyParty: true,
},
});

logger.info(
`Created alice user: ${aliceUser.id} with primary party (internal) ${aliceUser.primaryParty}`,
);
logger.info(
`Created bob user: ${bobUser.id} with primary party (internal) ${bobUser.primaryParty}`,
);
logger.info(
`Created master user: ${masterUser.id} with primary party (internal) ${masterUser.primaryParty}, with read as and execute as rights`,
);

const aliceSdk = await SDK.create({
auth: {
method: "self_signed",
issuer: "unsafe-auth",
credentials: {
clientId: aliceUser.id,
clientSecret: "unsafe",
audience: "https://canton.network.global",
scope: "",
},
},
ledgerClientUrl: localNetStaticConfig.LOCALNET_APP_USER_LEDGER_URL,
});

const aliceKeyPair = aliceSdk.keys.generate();
const aliceExternal = await aliceSdk.party.external
.create(aliceKeyPair.publicKey, {
partyHint: "v1-09-alice",
})
.sign(aliceKeyPair.privateKey)
.execute();

logger.info(`alice created external party`);

const bobSdk = await SDK.create({
auth: {
method: "self_signed",
issuer: "unsafe-auth",
credentials: {
clientId: bobUser.id,
clientSecret: "unsafe",
audience: "https://canton.network.global",
scope: "",
},
},
ledgerClientUrl: localNetStaticConfig.LOCALNET_APP_USER_LEDGER_URL,
});

const bobKeyPair = bobSdk.keys.generate();
const bobExternal = await bobSdk.party.external
.create(bobKeyPair.publicKey, {
partyHint: "v1-09-bob",
})
.sign(bobKeyPair.privateKey)
.execute();
logger.info(`bob created external party`);

const masterUserSdk = await SDK.create({
auth: {
method: "self_signed",
issuer: "unsafe-auth",
credentials: {
clientId: masterUser.id,
clientSecret: "unsafe",
audience: "https://canton.network.global",
scope: "",
},
},
ledgerClientUrl: localNetStaticConfig.LOCALNET_APP_USER_LEDGER_URL,
});

const masterWalletView = await masterUserSdk.party.list();

if (!masterWalletView?.find((p) => p === aliceExternal.partyId)) {
throw new Error("master user cannot see alice party");
}
if (!masterWalletView?.find((p) => p === bobExternal.partyId)) {
throw new Error("master user cannot see bob party");
}

const aliceWalletView = await aliceSdk.party.list();
logger.info(aliceWalletView);

if (aliceWalletView?.find((p) => p === bobExternal.partyId)) {
throw new Error("alice user can see bob party");
}

const bobWalletView = await bobSdk.party.list();

if (bobWalletView?.find((p) => p === aliceExternal.partyId)) {
throw new Error("bob user can see alice party");
}

logger.info(
"alice and bob have proper isolation and cannot see each others external parties",
);

//user management test
await bobSdk.user.rights.grant({
userRights: {
readAs: [aliceExternal.partyId],
},
});

const bobWalletViewAfterGrantRights = await bobSdk.party.list();

if (!bobWalletViewAfterGrantRights?.find((p) => p === aliceExternal.partyId)) {
throw new Error("bob user cannot see alice party even with ReadAs rights");
}

const bobRightsAfterGrantRights = await bobSdk.user.rights.list();

logger.info(bobRightsAfterGrantRights, "Bob user rights");

await bobSdk.user.rights.revoke({
userRights: {
readAs: [aliceExternal.partyId],
},
});

const bobWalletViewAfterRevokeRights = await bobSdk.party.list();

if (bobWalletViewAfterRevokeRights?.find((p) => p === aliceExternal.partyId)) {
throw new Error("bob user can see alice party even after revoking rights");
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -248,10 +248,8 @@ Each non-LocalNet environment requires different connection endpoints. Configure

- **JSON Ledger API URL** — The HTTP/JSON API endpoint for your validator's participant
- **gRPC Admin API URL** — The gRPC endpoint for participant administration
- **Validator API URL** — The validator app's REST API endpoint
- **Scan API URL** — The Scan service endpoint (either direct or via the BFT scan proxy)
- **Auth token** — A valid JWT token from your OIDC provider

<CantonDocsIntegrationsWalletConfigurationL70 />
- **Validator API URL** — The validator app's REST API endpoint (optional for token/amulet namespaces, if not provided will use the scan api)

See the [config template](https://github.com/canton-network/wallet-gateway/blob/main/docs/wallet-integration-guide/examples/snippets/config-template.ts) in the Wallet SDK repository for a complete example.
Loading