From fe5b8d412a64bd355844d861acbff110c7afcde8 Mon Sep 17 00:00:00 2001 From: Moritz Kiefer Date: Wed, 9 Feb 2022 14:28:28 +0100 Subject: [PATCH] Create alias contracts in frontend in create-daml-app MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Creating them in the setup script is nice but it makes it much harder to port this to Daml hub where we cannot rely on this. This also requires figuring out the public party in the frontend. I’ve chosen to infer it from the user rights which seems broadly sensible. For the backwards compat mode, I just hardcoded it because there isn’t a great way to figure it out. changelog_begin changelog_end --- templates/create-daml-app/daml/Setup.daml | 5 +---- templates/create-daml-app/daml/User.daml | 1 - .../create-daml-app/ui/src/Credentials.ts | 1 + .../src/components/LoginScreen.tsx.template | 20 +++++++++++++++++-- .../create-daml-app/ui/src/config.ts.template | 17 +++++++++++++++- 5 files changed, 36 insertions(+), 8 deletions(-) diff --git a/templates/create-daml-app/daml/Setup.daml b/templates/create-daml-app/daml/Setup.daml index 3564f5c7563c..4b58289c539e 100644 --- a/templates/create-daml-app/daml/Setup.daml +++ b/templates/create-daml-app/daml/Setup.daml @@ -5,8 +5,6 @@ import DA.Optional (fromSomeNote) import qualified DA.Text as T import Daml.Script -import User (Alias(..)) - -- | A test user for the create-daml-app network. data TestUser = TestUser with alias : Text @@ -24,7 +22,6 @@ createTestUser : TestUser -> Script Party createTestUser TestUser{alias, public} = do u <- getOrCreateUser alias (Some public) let p = getPrimaryParty u - submit p $ createCmd $ Alias p alias public pure p -- | Create the public party. @@ -44,7 +41,7 @@ getOrCreateUser alias publicM = do UserNotFound _ -> do p <- allocateParty alias let u = User userId (Some p) - createUser u $ [CanActAs p] ++ [CanReadAs public | Some public <- [publicM]] + createUser u $ CanActAs p :: [CanReadAs public | Some public <- [publicM]] pure u -- | Convert a text to a valid user id. diff --git a/templates/create-daml-app/daml/User.daml b/templates/create-daml-app/daml/User.daml index bca2311e7298..d6cf779011b1 100644 --- a/templates/create-daml-app/daml/User.daml +++ b/templates/create-daml-app/daml/User.daml @@ -6,7 +6,6 @@ module User where -- MAIN_TEMPLATE_BEGIN template User with username: Party - public: Party following: [Party] where signatory username diff --git a/templates/create-daml-app/ui/src/Credentials.ts b/templates/create-daml-app/ui/src/Credentials.ts index 64c0f8140bf0..08b5c7c7070a 100644 --- a/templates/create-daml-app/ui/src/Credentials.ts +++ b/templates/create-daml-app/ui/src/Credentials.ts @@ -6,6 +6,7 @@ import { User } from "@daml/ledger"; export type Credentials = { party: string; + publicParty: string; token: string; user: User; } diff --git a/templates/create-daml-app/ui/src/components/LoginScreen.tsx.template b/templates/create-daml-app/ui/src/components/LoginScreen.tsx.template index 23f8e8b98cef..c12709c870d3 100644 --- a/templates/create-daml-app/ui/src/components/LoginScreen.tsx.template +++ b/templates/create-daml-app/ui/src/components/LoginScreen.tsx.template @@ -14,6 +14,9 @@ type Props = { onLogin: (credentials: Credentials) => void; } +const toAlias = (userId: string): string => + userId.charAt(0).toUpperCase() + userId.slice(1); + /** * React component for the login screen of the `App`. */ @@ -24,9 +27,13 @@ const LoginScreen: React.FC = ({onLogin}) => { const ledger = new Ledger({token: credentials.token}); let userContract = await ledger.fetchByKey(User.User, credentials.party); if (userContract === null) { - const user = {username: credentials.party, following: [], public: 'public'}; + const user = {username: credentials.party, following: []}; userContract = await ledger.create(User.User, user); } + let userAlias = await ledger.fetchByKey(User.Alias, {_1: credentials.party, _2: credentials.publicParty}); + if (userAlias === null) { + await ledger.create(User.Alias, {username: credentials.party, alias: toAlias(credentials.user.userId), public: credentials.publicParty}); + } onLogin(credentials); } catch(error) { alert(`Unknown error:\n${JSON.stringify(error)}`); @@ -72,8 +79,14 @@ const LoginScreen: React.FC = ({onLogin}) => { alert(`Failed to login as '${username}':\n${errorMsg}`); throw error; }); + const publicParty:string = await auth.userManagement.publicParty(username, ledger).catch((error) => { + const errorMsg = error instanceof Error ? error.toString() : JSON.stringify(error); + alert(`Failed to login as '${username}':\n${errorMsg}`); + throw error; + }); await login({user: {userId: username, primaryParty: primaryParty}, party: primaryParty, + publicParty: publicParty, token: auth.makeToken(username)}); } @@ -100,7 +113,8 @@ const LoginScreen: React.FC = ({onLogin}) => { { if (creds) { - login({party:creds.party, user: {userId: creds.partyName, primaryParty: creds.party}, token:creds.token}); + // TODO (MK) Fix public party in Daml hub + login({party:creds.party, publicParty: "FIXME", user: {userId: creds.partyName, primaryParty: creds.party}, token:creds.token}); } }} options={{ @@ -120,9 +134,11 @@ const LoginScreen: React.FC = ({onLogin}) => { if (isLoading === false && isAuthenticated === true) { if (user !== undefined) { const party = user["https://daml.com/ledger-api"]; + // TODO (MK) Fix public party with Auth0 const creds: Credentials = { user: {userId: user.email ?? user.name ?? party, primaryParty: party}, party: party, + publicParty: "FIXME", token: (await getAccessTokenSilently({ audience: "https://daml.com/ledger-api"}))}; login(creds); diff --git a/templates/create-daml-app/ui/src/config.ts.template b/templates/create-daml-app/ui/src/config.ts.template index b456f8aa4c24..33e512be0c07 100644 --- a/templates/create-daml-app/ui/src/config.ts.template +++ b/templates/create-daml-app/ui/src/config.ts.template @@ -3,11 +3,12 @@ import { encode } from 'jwt-simple'; import { isRunningOnHub } from '@daml/hub-react'; -import Ledger from '@daml/ledger'; +import Ledger, { CanReadAs } from '@daml/ledger'; export type UserManagement = { tokenPayload: (loginName: string, ledgerId: string) => Object, primaryParty: (loginName: string, ledger: Ledger) => Promise, + publicParty: (loginName: string, ledger: Ledger) => Promise, }; export type Insecure = { @@ -39,6 +40,9 @@ export const noUserManagement: UserManagement = { } }), primaryParty: async (loginName: string, ledger: Ledger) => loginName, + // Without user management, we force a specific party id here because + // we mainly care about this for vmbc and there we can support this. + publicParty: async (loginName: string, ledger: Ledger) => 'public', }; // Used on SDK >= 2.0.0 with the exception of VMBC @@ -56,6 +60,17 @@ export const withUserManagement: UserManagement = { throw new Error(`User '${loginName}' has no primary party`); } + }, + publicParty: async (loginName, ledger: Ledger) => { + const rights = await ledger.listUserRights(); + const readAsRights: CanReadAs[] = rights.filter((x) : x is CanReadAs => x.type === "CanReadAs"); + if (readAsRights.length === 0) { + throw new Error(`User '${loginName} has no readAs claims for a public party`); + } else if (readAsRights.length > 1) { + throw new Error(`User '${loginName} has readAs claims for more than one party`); + } else { + return readAsRights[0].party; + } } };