Skip to content

Commit

Permalink
feat(claims): key value list when issuing credential to asset
Browse files Browse the repository at this point in the history
when issuing credential to asset (assiging role), issuer can add additional key - value list that will be stored offChain
  • Loading branch information
Passerino committed Oct 28, 2021
1 parent 9d76ca6 commit ab72d0d
Show file tree
Hide file tree
Showing 9 changed files with 218 additions and 106 deletions.
1 change: 1 addition & 0 deletions docs/api/classes/GnosisIam.GnosisIam-1.md
Expand Up @@ -1398,6 +1398,7 @@ ___
| Name | Type |
| :------ | :------ |
| `__namedParameters` | `Object` |
| `__namedParameters.claimParams?` | `Record`<`string`, `string`\> |
| `__namedParameters.id` | `string` |
| `__namedParameters.registrationTypes` | [`RegistrationTypes`](../enums/cacheServerClient_cacheServerClient_types.RegistrationTypes.md)[] |
| `__namedParameters.requester` | `string` |
Expand Down
1 change: 1 addition & 0 deletions docs/api/classes/iam.IAM.md
Expand Up @@ -1203,6 +1203,7 @@ ___
| Name | Type |
| :------ | :------ |
| `__namedParameters` | `Object` |
| `__namedParameters.claimParams?` | `Record`<`string`, `string`\> |
| `__namedParameters.id` | `string` |
| `__namedParameters.registrationTypes` | [`RegistrationTypes`](../enums/cacheServerClient_cacheServerClient_types.RegistrationTypes.md)[] |
| `__namedParameters.requester` | `string` |
Expand Down
Expand Up @@ -12,12 +12,19 @@

### Properties

- [claimParams](cacheServerClient_cacheServerClient_types.ClaimData.md#claimparams)
- [claimType](cacheServerClient_cacheServerClient_types.ClaimData.md#claimtype)
- [claimTypeVersion](cacheServerClient_cacheServerClient_types.ClaimData.md#claimtypeversion)
- [profile](cacheServerClient_cacheServerClient_types.ClaimData.md#profile)

## Properties

### claimParams

`Optional` **claimParams**: `Record`<`string`, `string`\>

___

### claimType

`Optional` **claimType**: `string`
Expand Down
1 change: 1 addition & 0 deletions src/cacheServerClient/cacheServerClient.types.ts
Expand Up @@ -92,6 +92,7 @@ export interface ClaimData extends Record<string, unknown> {
profile?: Profile;
claimType?: string;
claimTypeVersion?: number;
claimParams?: Record<string, string>;
}

export enum Order {
Expand Down
4 changes: 3 additions & 1 deletion src/iam.ts
Expand Up @@ -1627,12 +1627,14 @@ export class IAM extends IAMBase {
id,
subjectAgreement,
registrationTypes,
claimParams,
}: {
requester: string;
token: string;
id: string;
subjectAgreement: string;
registrationTypes: RegistrationTypes[];
claimParams?: Record<string, string>;
}) {
if (!this._did) {
throw new Error(ERROR_MESSAGES.USER_NOT_LOGGED_IN);
Expand Down Expand Up @@ -1661,7 +1663,7 @@ export class IAM extends IAMBase {
const publicClaim: IPublicClaim = {
did: sub,
signer: this._did,
claimData,
claimData: { ...claimData, ...(claimParams && { claimParams }) },
};
message.issuedToken = await this.issuePublicClaim({
publicClaim,
Expand Down
2 changes: 2 additions & 0 deletions test/claimsTests/claims.testSuite.ts
@@ -1,8 +1,10 @@
import { createIdentityTests } from "./createIdentityTests";
import { enrollmentClaimsTests } from "./enrollmentClaimsTests";
import { selfsignedClaimsTests } from "./selfsignedClaimsTests";
import { issueClaimRequestTests } from "./issueClaimRequestTests";

export const claimsTests = () => {
describe("Issue Claim Request tests", issueClaimRequestTests);
describe("Selfsigned claim tests", selfsignedClaimsTests);
describe("Enrollment claims tests", enrollmentClaimsTests);
describe("Create Identity tests", createIdentityTests);
Expand Down
97 changes: 97 additions & 0 deletions test/claimsTests/issueClaimRequestTests.ts
@@ -0,0 +1,97 @@
import { Wallet } from "ethers";
import { Methods } from "@ew-did-registry/did";
import jwtDecode from "jwt-decode";

import { IAM, IRoleDefinition, RegistrationTypes } from "../../src/iam-client-lib";
import { replenish, provider } from "../setup_contracts";
import { createIam, root, rootOwner } from "../iam.test";
import { mockJsonCodec, mockNats, restoreJsonCodec, restoreNats } from "../testUtils/mocks";

export const issueClaimRequestTests = (): void => {
const serviceProvider = Wallet.createRandom().connect(provider);
const patron = Wallet.createRandom().connect(provider);
const patronDID = `did:${Methods.Erc1056}:${patron.address}`;

const orgName = "orgname";
let serviceProviderIam: IAM;
let patronIam: IAM;
let rootOwnerIam: IAM;
const patronRole = "patronRole";

beforeAll(async () => {
await replenish(serviceProvider.address);
serviceProviderIam = await createIam(serviceProvider.privateKey, { createDocument: true });
await replenish(patron.address);
patronIam = await createIam(patron.privateKey, { createDocument: true });
await replenish(rootOwner.address);
rootOwnerIam = await createIam(rootOwner.privateKey, { createDocument: true });

const data: IRoleDefinition = {
fields: [],
issuer: {
issuerType: "DID",
did: [`did:${Methods.Erc1056}:${serviceProvider.address}`],
},
metadata: [],
roleName: patronRole,
roleType: "test",
version: 1,
enrolmentPreconditions: [],
};

await rootOwnerIam.createRole({
roleName: patronRole,
namespace: root,
data,
});
await rootOwnerIam.createOrganization({
orgName,
namespace: root,
data: { orgName },
returnSteps: false,
});
await rootOwnerIam.changeOrgOwnership({
namespace: `${orgName}.${root}`,
newOwner: serviceProvider.address,
});
});

afterAll(() => {
restoreNats();
restoreJsonCodec();
});

test("should issue claim request with additional params", async () => {
const { publish } = mockNats();
const jsonCodec = mockJsonCodec();
const registrationTypes = [RegistrationTypes.OnChain, RegistrationTypes.OffChain];

await patronIam.createClaimRequest({
claim: { claimType: `${patronRole}.${root}`, claimTypeVersion: 1, fields: [] },
registrationTypes,
});
const [, encodedMsg1] = publish.mock.calls.pop();
const { id, subjectAgreement, token } = jsonCodec.decode(encodedMsg1) as {
id;
subjectAgreement;
token;
};
const claimParams: Record<string, string> = {
"document ID": "ASG 123222",
DOB: "1990-01-07",
};
await serviceProviderIam.issueClaimRequest({
id,
registrationTypes,
requester: patronDID,
subjectAgreement,
token,
claimParams,
});

const [, encodedMsg2] = publish.mock.calls.pop();
const { issuedToken } = jsonCodec.decode(encodedMsg2) as { issuedToken };
const data = jwtDecode<{ claimData: { claimParams } }>(issuedToken);
expect(data.claimData.claimParams).toEqual(claimParams);
});
};
143 changes: 72 additions & 71 deletions test/organization.testSuite.ts
Expand Up @@ -9,85 +9,86 @@ import { chainConfigs } from "../src/iam/chainConfig";
export const org1 = "org1";

export const orgTests = () => {
const orgName = "Organization 1";
const orgName = "Organization 1";

test("can create organization", async () => {
await rootOwnerIam.createOrganization({ orgName: org1, namespace: root, data: { orgName } });
test("can create organization", async () => {
await rootOwnerIam.createOrganization({ orgName: org1, namespace: root, data: { orgName } });

expect(await rootOwnerIam.checkExistenceOfDomain({ domain: `${org1}.${root}` })).toBe(true);
expect(
await rootOwnerIam.checkExistenceOfDomain({
domain: `${ENSNamespaceTypes.Application}.${org1}.${root}`
})
).toBe(true);
expect(
await rootOwnerIam.checkExistenceOfDomain({ domain: `${ENSNamespaceTypes.Roles}.${org1}.${root}` })
).toBe(true);
expect(await rootOwnerIam.getSubdomains({ domain: root })).toContain(`${org1}.${root}`);
expect(await rootOwnerIam.isOwner({ domain: `${org1}.${root}`, user: rootOwner.address }));
});

test("suborganization can be created", async () => {
const org1_1 = "org1-1";
await rootOwnerIam.createOrganization({
orgName: org1_1,
data: {
orgName: "Organization 1_1"
},
namespace: `${org1}.${root}`
expect(await rootOwnerIam.checkExistenceOfDomain({ domain: `${org1}.${root}` })).toBe(true);
expect(
await rootOwnerIam.checkExistenceOfDomain({
domain: `${ENSNamespaceTypes.Application}.${org1}.${root}`,
}),
).toBe(true);
expect(
await rootOwnerIam.checkExistenceOfDomain({ domain: `${ENSNamespaceTypes.Roles}.${org1}.${root}` }),
).toBe(true);
expect(await rootOwnerIam.getSubdomains({ domain: root })).toContain(`${org1}.${root}`);
expect(await rootOwnerIam.isOwner({ domain: `${org1}.${root}`, user: rootOwner.address }));
});

expect(await rootOwnerIam.checkExistenceOfDomain({ domain: `${org1_1}.${org1}.${root}` })).toBe(true);
expect(await rootOwnerIam.getSubdomains({ domain: `${org1}.${root}` })).toContain(
`${org1_1}.${org1}.${root}`
);
});
test("suborganization can be created", async () => {
const org1_1 = "org1-1";
await rootOwnerIam.createOrganization({
orgName: org1_1,
data: {
orgName: "Organization 1_1",
},
namespace: `${org1}.${root}`,
});

test("org role can be created", async () => {
const namespace = `${org1}.${root}`;
const roleName = `${org1}-role1`;
const roleDomain = `${roleName}.${namespace}`;
const roleNode = namehash(roleDomain);
expect(await rootOwnerIam.checkExistenceOfDomain({ domain: `${org1_1}.${org1}.${root}` })).toBe(true);
expect(await rootOwnerIam.getSubdomains({ domain: `${org1}.${root}` })).toContain(`${org1_1}.${org1}.${root}`);
});

const data: IRoleDefinition = {
fields: [],
issuer: {
issuerType: "DID",
did: [`did:${Methods.Erc1056}:${rootOwner.address}`]
},
metadata: [],
roleName,
roleType: "test",
version: 1,
enrolmentPreconditions: [{
type: PreconditionTypes.Role,
conditions: [roleDomain] // Circular condition but sufficient for test
}]
};
test("org role can be created", async () => {
const namespace = `${org1}.${root}`;
const roleName = `${org1}-role1`;
const roleDomain = `${roleName}.${namespace}`;
const roleNode = namehash(roleDomain);

await rootOwnerIam.createRole({
roleName,
namespace,
data
});
const data: IRoleDefinition = {
fields: [],
issuer: {
issuerType: "DID",
did: [`did:${Methods.Erc1056}:${rootOwner.address}`],
},
metadata: [],
roleName,
roleType: "test",
version: 1,
enrolmentPreconditions: [
{
type: PreconditionTypes.Role,
conditions: [roleDomain], // Circular condition but sufficient for test
},
],
};

const roleDef = await rootOwnerIam.getDefinition({
namespace: roleDomain,
type: ENSNamespaceTypes.Roles
});
expect(roleDef).toMatchObject<IRoleDefinition>(data);
await rootOwnerIam.createRole({
roleName,
namespace,
data,
});

const roleDef = await rootOwnerIam.getDefinition({
namespace: roleDomain,
type: ENSNamespaceTypes.Roles,
});
expect(roleDef).toMatchObject<IRoleDefinition>(data);

const reverseName = await ensResolver.name(roleNode);
expect(reverseName).toEqual(roleDomain);
const reverseName = await ensResolver.name(roleNode);
expect(reverseName).toEqual(roleDomain);

const resolver = await ensRegistry.resolver(roleNode);
const actualTypes = (await rootOwnerIam.registrationTypesOfRoles([roleDomain]))[roleDomain];
const { chainId } = await provider.getNetwork();
const expectedTypes = resolver === chainConfigs[chainId].ensPublicResolverAddress ?
new Set([RegistrationTypes.OffChain]) :
resolver === chainConfigs[chainId].ensResolverAddress ?
new Set([RegistrationTypes.OffChain, RegistrationTypes.OnChain]) :
[];
expect(actualTypes).toEqual(expectedTypes);
});
const resolver = await ensRegistry.resolver(roleNode);
const actualTypes = (await rootOwnerIam.registrationTypesOfRoles([roleDomain]))[roleDomain];
const { chainId } = await provider.getNetwork();
const expectedTypes =
resolver === chainConfigs[chainId].ensPublicResolverAddress
? new Set([RegistrationTypes.OffChain])
: resolver === chainConfigs[chainId].ensResolverAddress
? new Set([RegistrationTypes.OffChain, RegistrationTypes.OnChain])
: [];
expect(actualTypes).toEqual(expectedTypes);
});
};

0 comments on commit ab72d0d

Please sign in to comment.