diff --git a/dfx/index.ts b/dfx/index.ts index ec828821bd..1d32fd6ac8 100644 --- a/dfx/index.ts +++ b/dfx/index.ts @@ -60,8 +60,24 @@ export function getIdentityName(): string { return execSync(`dfx identity whoami`).toString().trim(); } -export function generateIdentity(name: string) { - execSync(`dfx identity new ${name} --storage-mode plaintext`); +type StorageMode = 'keyring' | 'password-protected' | 'plaintext'; + +export function generateIdentity( + name: string, + storageMode?: StorageMode +): Buffer { + if (storageMode === undefined) { + console.info( + `Generating identity "${name}". You may have to create a password for ${name}.` + ); + return execSync(`dfx identity new ${name}`); + } + if (storageMode === 'password-protected') { + console.info( + `Generating identity "${name}". You will have to create a password for ${name}.` + ); + } + return execSync(`dfx identity new ${name} --storage-mode ${storageMode}`); } export function useIdentity(name: string) { @@ -75,11 +91,16 @@ export function getIdentities(): string[] { return identities; } +export function identityExists(name: string): boolean { + const identities = getIdentities(); + return identities.includes(name); +} + export function removeIdentity(name: string) { execSync(`dfx identity remove ${name}`); } -export async function getIdentity( +export async function getIdentityFromPemFile( identityName: string = getIdentityName() ): Promise { const identityPath = join( @@ -92,3 +113,41 @@ export async function getIdentity( ); return Secp256k1KeyIdentity.fromPem(await readFile(identityPath, 'utf-8')); } + +export function getPemKey(identityName: string = getIdentityName()): string { + console.info( + `Getting Pem Key for ${identityName}. The password for ${identityName} may be required` + ); + const cmd = `dfx identity export ${identityName}`; + const result = execSync(cmd, { + stdio: ['inherit', 'pipe', 'inherit'] // TODO I would prefer it to pipe the stderr but it will fail immediately if you do that + }) + .toString() + .trim(); + return result; +} + +export function getIdentity(identityName?: string): Secp256k1KeyIdentity { + return Secp256k1KeyIdentity.fromPem(getPemKey(identityName)); +} + +export function getPrincipal(identityName: string = getIdentityName()): string { + console.info( + `Getting Principal for ${identityName}. The password for ${identityName} may be required` + ); + const cmd = `dfx identity get-principal --identity ${identityName}`; + return execSync(cmd, { + stdio: ['inherit', 'pipe', 'pipe'] + }) + .toString() + .trim(); +} + +export function addController(canisterName: string, principal: string) { + const currentIdentity = getIdentityName(); + console.info( + `Adding controller. You may need to enter the password for ${currentIdentity} at this point.` + ); + const cmd = `dfx canister update-settings ${canisterName} --add-controller ${principal}`; + return execSync(cmd, { stdio: ['inherit', 'pipe', 'inherit'] }); // TODO I would prefer it to pipe the stderr but it will fail immediately if you do that +} diff --git a/src/compiler/file_uploader/index.ts b/src/compiler/file_uploader/index.ts index 75109bab10..703d2a3b99 100644 --- a/src/compiler/file_uploader/index.ts +++ b/src/compiler/file_uploader/index.ts @@ -3,13 +3,15 @@ import { expandPaths } from './expand_paths'; import { uploadFile } from './upload_file'; import { onBeforeExit } from './on_before_exit'; import { createActor } from './uploader_actor'; +import { generateUploaderIdentity } from './uploader_identity'; export type Src = string; export type Dest = string; export async function uploadFiles(canisterName: string, paths: [Src, Dest][]) { const canisterId = getCanisterId(canisterName); - const actor = await createActor(canisterId); + const identityName = generateUploaderIdentity(canisterName); + const actor = await createActor(canisterId, identityName); const chunkSize = 2_000_000; // The current message limit is about 2 MB diff --git a/src/compiler/file_uploader/uploader_actor.ts b/src/compiler/file_uploader/uploader_actor.ts index c1bb40ddbe..91d237fdfb 100644 --- a/src/compiler/file_uploader/uploader_actor.ts +++ b/src/compiler/file_uploader/uploader_actor.ts @@ -1,11 +1,12 @@ import { Actor, ActorMethod, ActorSubclass } from '@dfinity/agent'; import { createAuthenticatedAgent } from '../../../dfx'; +import { AZLE_UPLOADER_IDENTITY_NAME } from './uploader_identity'; export type UploaderActor = ActorSubclass<_SERVICE>; export async function createActor( canisterId: string, - identityName?: string + identityName: string = AZLE_UPLOADER_IDENTITY_NAME ): Promise { const agent = await createAuthenticatedAgent(identityName); diff --git a/src/compiler/file_uploader/uploader_identity.ts b/src/compiler/file_uploader/uploader_identity.ts new file mode 100644 index 0000000000..d24a2b51c2 --- /dev/null +++ b/src/compiler/file_uploader/uploader_identity.ts @@ -0,0 +1,20 @@ +import { + addController, + generateIdentity, + getPrincipal, + identityExists +} from '../../../dfx'; + +export const AZLE_UPLOADER_IDENTITY_NAME = '__azle__fileUploaderIdentity'; + +export function generateUploaderIdentity(canisterName: string): string { + if (!identityExists(AZLE_UPLOADER_IDENTITY_NAME)) { + generateIdentity(AZLE_UPLOADER_IDENTITY_NAME); + } + + const principal = getPrincipal(AZLE_UPLOADER_IDENTITY_NAME); + + addController(canisterName, principal); + + return AZLE_UPLOADER_IDENTITY_NAME; +}