Skip to content

Commit

Permalink
reduce complexity of identity api, it is now scoped to the agent as a…
Browse files Browse the repository at this point in the history
… tenant
  • Loading branch information
LiranCohen committed Sep 26, 2024
1 parent e2df13a commit fcaf10f
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 129 deletions.
8 changes: 8 additions & 0 deletions packages/agent/src/did-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ export class AgentDidApi<TKeyManager extends AgentKeyManager = AgentKeyManager>
// Create the DID and store the generated keys in the Agent's key manager.
const bearerDid = await didMethod.create({ keyManager: this.agent.keyManager, options });

// pre-populate the resolution cache with the document and metadata
await this.cache.set(bearerDid.uri, { didDocument: bearerDid.document, didResolutionMetadata: { }, didDocumentMetadata: bearerDid.metadata });

// Persist the DID to the store, by default, unless the `store` option is set to false.
if (store ?? true) {
// Data stored in the Agent's DID store must be in PortableDid format.
Expand Down Expand Up @@ -260,6 +263,8 @@ export class AgentDidApi<TKeyManager extends AgentKeyManager = AgentKeyManager>
const { uri, document, metadata } = bearerDid;
const portableDidWithoutKeys: PortableDid = { uri, document, metadata };

await this.cache.set(uri, { didDocument: document, didResolutionMetadata: { }, didDocumentMetadata: metadata });

// Store the DID in the agent's DID store.
// Unless an existing `tenant` is specified, a record that includes the DID's URI, document,
// and metadata will be stored under a new tenant controlled by the imported DID.
Expand All @@ -285,6 +290,9 @@ export class AgentDidApi<TKeyManager extends AgentKeyManager = AgentKeyManager>
throw new Error('AgentDidApi: Could not delete, DID not found');
}

// delete from the cache
await this.cache.delete(didUri);

// Delete the data before deleting the associated keys.
await this._store.delete({ id: didUri, agent: this.agent, tenant });

Expand Down
68 changes: 22 additions & 46 deletions packages/agent/src/identity-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export interface IdentityCreateParams<
metadata: RequireOnly<IdentityMetadata, 'name'>;
didMethod?: TMethod;
didOptions?: DidMethodCreateOptions<TKeyManager>[TMethod];
tenant?: string;
store?: boolean;
}

Expand Down Expand Up @@ -71,22 +70,29 @@ export class AgentIdentityApi<TKeyManager extends AgentKeyManager = AgentKeyMana
this._agent = agent;
}

public async create({ metadata, didMethod = 'dht', didOptions, store, tenant }:
get tenant(): string {
if (!this._agent) {
throw new Error('AgentIdentityApi: The agent must be set to perform tenant specific actions.');
}

return this._agent.agentDid.uri;
}

public async create({ metadata, didMethod = 'dht', didOptions, store }:
IdentityCreateParams<TKeyManager>
): Promise<BearerIdentity> {
// Unless an existing `tenant` is specified, a record that includes the DID's URI, document,
// and metadata will be stored under a new tenant controlled by the newly created DID.

const bearerDid = await this.agent.did.create({
method : didMethod,
options : didOptions,
tenant : this.tenant,
store,
tenant
});

// Create the BearerIdentity object.
const identity = new BearerIdentity({
did : bearerDid,
metadata : { ...metadata, uri: bearerDid.uri, tenant: tenant ?? bearerDid.uri }
metadata : { ...metadata, uri: bearerDid.uri, tenant: this.tenant }
});

// Persist the Identity to the store, by default, unless the `store` option is set to false.
Expand All @@ -104,12 +110,12 @@ export class AgentIdentityApi<TKeyManager extends AgentKeyManager = AgentKeyMana
return identity;
}

public async export({ didUri, tenant }: {
public async export({ didUri }: {
didUri: string;
tenant?: string;
}): Promise<PortableIdentity> {

// Attempt to retrieve the Identity from the Agent's Identity store.
const bearerIdentity = await this.get({ didUri, tenant });
const bearerIdentity = await this.get({ didUri });

if (!bearerIdentity) {
throw new Error(`AgentIdentityApi: Failed to export due to Identity not found: ${didUri}`);
Expand All @@ -122,12 +128,11 @@ export class AgentIdentityApi<TKeyManager extends AgentKeyManager = AgentKeyMana
return portableIdentity;
}

public async get({ didUri, tenant }: {
public async get({ didUri }: {
didUri: string;
tenant?: string;
}): Promise<BearerIdentity | undefined> {
// Attempt to retrieve the Identity from the Agent's Identity store.
const storedIdentity = await this._store.get({ id: didUri, agent: this.agent, tenant, useCache: true });
const storedIdentity = await this._store.get({ id: didUri, agent: this.agent, tenant: this.tenant, useCache: true });

// If the Identity is not found in the store, return undefined.
if (!storedIdentity) return undefined;
Expand All @@ -150,6 +155,10 @@ export class AgentIdentityApi<TKeyManager extends AgentKeyManager = AgentKeyMana
public async import({ portableIdentity }: {
portableIdentity: PortableIdentity;
}): Promise<BearerIdentity> {

// set the tenant of the portable identity to the agent's tenant
portableIdentity.metadata.tenant = this.tenant;

// Import the PortableDid to the Agent's DID store.
const storedDid = await this.agent.did.import({
portableDid : portableIdentity.portableDid,
Expand Down Expand Up @@ -183,44 +192,11 @@ export class AgentIdentityApi<TKeyManager extends AgentKeyManager = AgentKeyMana
// Retrieve the list of Identities from the Agent's Identity store.
const storedIdentities = await this._store.list({ agent: this.agent, tenant });

const identities = await Promise.all(
storedIdentities.map(async metadata => {
return this.get({ didUri: metadata.uri, tenant: metadata.tenant });
})
);
const identities = await Promise.all(storedIdentities.map(metadata => this.get({ didUri: metadata.uri })));

return identities.filter(identity => typeof identity !== 'undefined') as BearerIdentity[];
}

public async manage({ portableIdentity }: {
portableIdentity: PortableIdentity;
}): Promise<BearerIdentity> {
// Retrieve the DID using the `tenant` stored in the given Identity's metadata.
const storedDid = await this.agent.did.get({
didUri : portableIdentity.metadata.uri,
tenant : portableIdentity.metadata.tenant
});

// Verify the DID is present in the DID store.
if (!storedDid) {
throw new Error(`AgentIdentityApi: Failed to manage Identity: ${portableIdentity.metadata.uri}`);
}

// Create the BearerIdentity object.
const identity = new BearerIdentity({ did: storedDid, metadata: portableIdentity.metadata });

// Store the Identity metadata in the Agent's Identity store.
await this._store.set({
id : identity.did.uri,
data : identity.metadata,
agent : this.agent,
preventDuplicates : true,
useCache : true
});

return identity;
}

public async delete({ didUri, tenant }:{
didUri: string;
tenant?: string;
Expand Down
7 changes: 0 additions & 7 deletions packages/agent/src/test-harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,13 +170,6 @@ export class PlatformAgentTestHarness {
return bearerIdentity;
}

public async preloadResolverCache({ didUri, resolutionResult }: {
didUri: string;
resolutionResult: DidResolutionResult;
}): Promise<void> {
await this.didResolverCache.set(didUri, resolutionResult);
}

public static async setup({ agentClass, agentStores, testDataLocation }: {
agentClass: new (params: any) => Web5PlatformAgent<LocalKeyManager>
agentStores?: 'dwn' | 'memory';
Expand Down
20 changes: 0 additions & 20 deletions packages/agent/tests/dwn-api.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1082,15 +1082,6 @@ describe('AgentDwnApi', () => {
}
};

await testHarness.preloadResolverCache({
didUri : testPortableIdentity.portableDid.uri,
resolutionResult : {
didDocument : testPortableIdentity.portableDid.document,
didDocumentMetadata : testPortableIdentity.portableDid.metadata,
didResolutionMetadata : {}
}
});

alice = await testHarness.agent.identity.import({
portableIdentity: testPortableIdentity
});
Expand Down Expand Up @@ -1840,17 +1831,6 @@ describe('AgentDwnApi', () => {
store : false
});

// Since the DID DHT document wasn't published, add the DID DHT document to the resolver
// cache so that DID resolution will succeed during the dereferencing operation.
await testHarness.preloadResolverCache({
didUri : identity.did.uri,
resolutionResult : {
didDocument : identity.did.document,
didDocumentMetadata : identity.did.metadata,
didResolutionMetadata : {}
}
});

try {
await testHarness.agent.dwn.sendRequest({
author : identity.did.uri,
Expand Down
44 changes: 4 additions & 40 deletions packages/agent/tests/identity-api.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,53 +83,20 @@ describe('AgentIdentityApi', () => {
});
});

describe('manage()' , () => {
it('imports only the Identity Metadata to Agent tenant', async () => {
// Create a new Identity, which by default is stored under the tenant of the created DID.
const identity = await testHarness.agent.identity.create({
didMethod : 'jwk',
metadata : { name: 'Test Identity' },
});

// Verify that the Identity is stored under the new Identity's tenant.
let storedIdentity = await testHarness.agent.identity.get({ didUri: identity.did.uri, tenant: identity.did.uri });
expect(storedIdentity).to.exist;

// Add a managed Identity to the Agent's tenant.
const managedIdentity = await testHarness.agent.identity.manage({
portableIdentity: await identity.export()
});
expect(managedIdentity).to.deep.equal(identity);

// Verify that the Identity Metadata is stored under the Agent's tenant.
storedIdentity = await testHarness.agent.identity.get({ didUri: identity.did.uri });
expect(storedIdentity).to.exist;

// Verify the DID ONLY exists under the tenant of the previously created DID.
let storedDidAgent = await testHarness.agent.did.get({ didUri: identity.did.uri });
expect(storedDidAgent).to.not.exist;
let storedDidNewIdentity = await testHarness.agent.did.get({ didUri: identity.did.uri, tenant: identity.did.uri });
expect(storedDidNewIdentity).to.exist;
});
});

describe('list()', () => {
it('returns an array of all identities', async () => {
// Create three new identities all under the Agent's tenant.
const alice = await testHarness.agent.identity.create({
didMethod : 'jwk',
metadata : { name: 'Alice' },
tenant : testHarness.agent.agentDid.uri
});
const bob = await testHarness.agent.identity.create({
didMethod : 'jwk',
metadata : { name: 'Bob' },
tenant : testHarness.agent.agentDid.uri
});
const carol = await testHarness.agent.identity.create({
didMethod : 'jwk',
metadata : { name: 'Carol' },
tenant : testHarness.agent.agentDid.uri
});

// List identities and verify the result.
Expand Down Expand Up @@ -159,19 +126,19 @@ describe('AgentIdentityApi', () => {
});

// Verify that the Identity exists.
let storedIdentity = await testHarness.agent.identity.get({ didUri: identity.did.uri, tenant: identity.did.uri });
let storedIdentity = await testHarness.agent.identity.get({ didUri: identity.did.uri });
expect(storedIdentity).to.exist;
expect(storedIdentity?.did.uri).to.equal(identity.did.uri);

// Delete the Identity.
await testHarness.agent.identity.delete({ didUri: identity.did.uri, tenant: identity.did.uri });
await testHarness.agent.identity.delete({ didUri: identity.did.uri });

// Verify that the Identity no longer exists.
storedIdentity = await testHarness.agent.identity.get({ didUri: identity.did.uri, tenant: identity.did.uri });
storedIdentity = await testHarness.agent.identity.get({ didUri: identity.did.uri });
expect(storedIdentity).to.not.exist;

// Verify that the DID still exists
const storedDid = await testHarness.agent.did.get({ didUri: identity.did.uri, tenant: identity.did.uri });
const storedDid = await testHarness.agent.did.get({ didUri: identity.did.uri });
expect(storedDid).to.not.be.undefined;
expect(storedDid!.uri).to.equal(identity.did.uri);
});
Expand Down Expand Up @@ -212,7 +179,6 @@ describe('AgentIdentityApi', () => {
await testHarness.agent.identity.create({
didMethod : 'jwk',
metadata : { name: 'Alice' },
tenant : testHarness.agent.agentDid.uri
});

// attempt to get a connected identity when none exist
Expand All @@ -223,14 +189,12 @@ describe('AgentIdentityApi', () => {
const connectedDid1 = await testHarness.agent.identity.create({
didMethod : 'jwk',
metadata : { name: 'Bob', connectedDid: 'did:method:abc123' },
tenant : testHarness.agent.agentDid.uri
});

// Create another connected Identity.
const connectedDid2 = await testHarness.agent.identity.create({
didMethod : 'jwk',
metadata : { name: 'Carol', connectedDid: 'did:method:def456' },
tenant : testHarness.agent.agentDid.uri
});

// get the first connected identity
Expand Down
2 changes: 0 additions & 2 deletions packages/agent/tests/permissions-api.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,9 @@ describe('AgentPermissionsApi', () => {

// Create an "alice" Identity to author the DWN messages.
const alice = await testHarness.agent.identity.create({ didMethod: 'jwk', metadata: { name: 'Alice' } });
await testHarness.agent.identity.manage({ portableIdentity: await alice.export() });
aliceDid = alice.did;

const bob = await testHarness.agent.identity.create({ didMethod: 'jwk', metadata: { name: 'Bob' } });
await testHarness.agent.identity.manage({ portableIdentity: await bob.export() });
bobDid = bob.did;
});

Expand Down
26 changes: 12 additions & 14 deletions packages/agent/tests/store-identity.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,15 @@ describe('IdentityStore', () => {

// Test deleting the Identity and validate the result.
const deleteResult = await identityStore.delete({
id : identity.did.uri,
tenant : identity.did.uri,
agent : testHarness.agent
id : identity.did.uri,
agent : testHarness.agent
});
expect(deleteResult).to.be.true;

// Verify the Identity is no longer in the store.
const storedIdentity = await identityStore.get({
id : identity.did.uri,
tenant : identity.did.uri,
agent : testHarness.agent
id : identity.did.uri,
agent : testHarness.agent
});
expect(storedIdentity).to.be.undefined;
});
Expand Down Expand Up @@ -116,7 +114,7 @@ describe('IdentityStore', () => {
});

// Test getting the Identity.
const storedIdentity = await identityStore.get({ id: identity.did.uri, tenant: identity.did.uri, agent: testHarness.agent });
const storedIdentity = await identityStore.get({ id: identity.did.uri, agent: testHarness.agent });

// Verify the Identity is in the store.
expect(storedIdentity).to.exist;
Expand Down Expand Up @@ -155,9 +153,9 @@ describe('IdentityStore', () => {
describe('list()', () => {
it('should return an array of all Identities in the store', async () => {
// Generate three new Identities that are stored under the Agent's context.
const bearerIdentity1 = await testHarness.agent.identity.create({ didMethod: 'jwk', metadata: { name: 'Test Identity 1' }, tenant: testHarness.agent.agentDid.uri });
const bearerIdentity2 = await testHarness.agent.identity.create({ didMethod: 'jwk', metadata: { name: 'Test Identity 2' }, tenant: testHarness.agent.agentDid.uri });
const bearerIdentity3 = await testHarness.agent.identity.create({ didMethod: 'jwk', metadata: { name: 'Test Identity 3' }, tenant: testHarness.agent.agentDid.uri });
const bearerIdentity1 = await testHarness.agent.identity.create({ didMethod: 'jwk', metadata: { name: 'Test Identity 1' } });
const bearerIdentity2 = await testHarness.agent.identity.create({ didMethod: 'jwk', metadata: { name: 'Test Identity 2' } });
const bearerIdentity3 = await testHarness.agent.identity.create({ didMethod: 'jwk', metadata: { name: 'Test Identity 3' } });

// List Identities and verify the result.
const storedDids = await identityStore.list({ agent: testHarness.agent });
Expand All @@ -183,12 +181,12 @@ describe('IdentityStore', () => {
await testHarness.agent.keyManager.importKey({ key: authorDid.privateKeys![0] });

// Generate three new Identities that are stored under the custom author context.
const bearerIdentity1 = await testHarness.agent.identity.create({ didMethod: 'jwk', metadata: { name: 'Test Identity 1' }, tenant: authorDid.uri });
const bearerIdentity2 = await testHarness.agent.identity.create({ didMethod: 'jwk', metadata: { name: 'Test Identity 2' }, tenant: authorDid.uri });
const bearerIdentity3 = await testHarness.agent.identity.create({ didMethod: 'jwk', metadata: { name: 'Test Identity 3' }, tenant: authorDid.uri });
const bearerIdentity1 = await testHarness.agent.identity.create({ didMethod: 'jwk', metadata: { name: 'Test Identity 1' } });
const bearerIdentity2 = await testHarness.agent.identity.create({ didMethod: 'jwk', metadata: { name: 'Test Identity 2' } });
const bearerIdentity3 = await testHarness.agent.identity.create({ didMethod: 'jwk', metadata: { name: 'Test Identity 3' } });

// List Identities and verify the result.
const storedDids = await identityStore.list({ tenant: authorDid.uri, agent: testHarness.agent });
const storedDids = await identityStore.list({ agent: testHarness.agent });
expect(storedDids).to.have.length(3);
const importedDids = [bearerIdentity1.did.uri, bearerIdentity2.did.uri, bearerIdentity3.did.uri];
for (const storedIdentity of storedDids) {
Expand Down

0 comments on commit fcaf10f

Please sign in to comment.