Skip to content

Commit

Permalink
feat(claims): verify role vc
Browse files Browse the repository at this point in the history
  • Loading branch information
JGiter committed May 26, 2022
1 parent 2a1680c commit b318dbd
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 23 deletions.
47 changes: 33 additions & 14 deletions e2e/claims.service.e2e.ts
Expand Up @@ -20,6 +20,7 @@ import {
MessagingService,
IClaimIssuance,
SignerT,
Claim,
} from '../src';
import { replenish, root, rpcUrl, setupENS } from './utils/setup-contracts';
import { ClaimManager__factory } from '../ethers/factories/ClaimManager__factory';
Expand Down Expand Up @@ -86,6 +87,8 @@ const mockCachedDocument = jest.fn().mockImplementation((did: string) => {
],
}; // all documents are created
});
const getClamsBySubjectInitMock: (did: string) => Partial<Claim>[] = () => [];

const mockGetCachedOwnedAssets = jest.fn();
const mockGetAssetById = jest.fn();
const mockGetClaimsBySubject = jest.fn();
Expand Down Expand Up @@ -123,7 +126,7 @@ jest.mock('../src/modules/messaging/messaging.service', () => {
};
});

describe('Enrollment claim tests', () => {
describe('Сlaim tests', () => {
let claimsService: ClaimsService;
let signerService: SignerService;
let assetsService: AssetsService;
Expand All @@ -133,7 +136,7 @@ describe('Enrollment claim tests', () => {

beforeEach(async () => {
jest.clearAllMocks();
//mockGetClaimsBySubject.mockReset();
mockGetClaimsBySubject.mockImplementation(getClamsBySubjectInitMock);
await replenish(await staticIssuer.getAddress());
await replenish(await rootOwner.getAddress());
await replenish(await dynamicIssuer.getAddress());
Expand Down Expand Up @@ -189,7 +192,7 @@ describe('Enrollment claim tests', () => {
setLogger(new ConsoleLogger(LogLevel.warn));
});

describe('Enrollment tests', () => {
describe('Role claim tests', () => {
async function enrolAndIssue(
requestSigner: Required<SignerT>,
issueSigner: Required<SignerT>,
Expand All @@ -211,7 +214,11 @@ describe('Enrollment claim tests', () => {
const requesterDID = signerService.did;
const requestorFields = [{ key: 'temperature', value: 36 }];
await claimsService.createClaimRequest({
claim: { claimType, claimTypeVersion: version, requestorFields },
claim: {
claimType,
claimTypeVersion: version,
requestorFields,
},
registrationTypes,
subject: subjectDID,
});
Expand All @@ -228,6 +235,22 @@ describe('Enrollment claim tests', () => {
mockIssueClaim.mock.calls.pop()
);

const currentGetClaimsBySubjectMock =
mockGetClaimsBySubject.getMockImplementation() as jest.Mock;
mockGetClaimsBySubject.mockImplementation((did) =>
[...currentGetClaimsBySubjectMock(did)].concat(
did === subjectDID
? [
{
claimType,
claimTypeVersion: version,
issuedToken: issuedClaim.issuedToken,
},
]
: []
)
);

const {
issuedToken,
requester,
Expand Down Expand Up @@ -409,6 +432,11 @@ describe('Enrollment claim tests', () => {
await enrolAndIssue(dynamicIssuer, staticIssuer, {
subjectDID: dynamicIssuerDID,
claimType: `${roleName1}.${root}`,
registrationTypes: [
RegistrationTypes.OnChain,
RegistrationTypes.OffChain, // role type issuer should have offchain claim
],
issuerFields: [],
});

expect(
Expand All @@ -423,7 +451,6 @@ describe('Enrollment claim tests', () => {
subjectDID: rootOwnerDID,
claimType: `${roleName2}.${root}`,
});

return expect(
await claimsService.hasOnChainRole(
rootOwnerDID,
Expand All @@ -442,16 +469,10 @@ describe('Enrollment claim tests', () => {
returnSteps: false,
});

const role1Claim = {
claimType: `${roleName1}.${root}`,
isAccepted: true,
};
await enrolAndIssue(rootOwner, staticIssuer, {
subjectDID: rootOwnerDID,
claimType: `${roleName1}.${root}`,
});
mockGetClaimsBySubject.mockImplementationOnce(() => [role1Claim]); // to verify requesting
mockGetClaimsBySubject.mockImplementationOnce(() => [role1Claim]); // to verify issuance

await enrolAndIssue(rootOwner, staticIssuer, {
subjectDID: rootOwnerDID,
Expand All @@ -475,8 +496,6 @@ describe('Enrollment claim tests', () => {
returnSteps: false,
});

mockGetClaimsBySubject.mockImplementationOnce(() => []);

return expect(
enrolAndIssue(rootOwner, staticIssuer, {
subjectDID: rootOwnerDID,
Expand Down Expand Up @@ -567,7 +586,7 @@ describe('Enrollment claim tests', () => {
await claimsService.createClaimRequest({
claim: {
claimType: `${roleName1}.${root}`,
claimTypeVersion: 1,
claimTypeVersion: version,
requestorFields: [],
},
registrationTypes,
Expand Down
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Expand Up @@ -65,6 +65,7 @@
"@energyweb/ekc": "^0.6.6",
"@energyweb/onchain-claims": "^1.0.1-alpha.46.0",
"@energyweb/staking-pool": "^1.0.0-rc.14",
"@energyweb/vc-verification": "^1.0.1-alpha.36.0",
"@ensdomains/ens": "^0.6.2",
"@ew-did-registry/claims": "^0.6.3-alpha.577.0",
"@ew-did-registry/credentials-interface": "^0.6.3-alpha.577.0",
Expand Down
61 changes: 52 additions & 9 deletions src/modules/claims/claims.service.ts
Expand Up @@ -6,6 +6,12 @@ import {
IRoleDefinition,
PreconditionType,
} from '@energyweb/credential-governance';
import {
CredentialResolver,
EthersProviderIssuerDefinitionResolver,
IpfsCredentialResolver,
VCIssuerVerification,
} from '@energyweb/vc-verification';
import { ClaimRevocation } from '@energyweb/onchain-claims';
import { Methods } from '@ew-did-registry/did';
import { Algorithms } from '@ew-did-registry/jwt';
Expand All @@ -16,6 +22,7 @@ import {
IServiceEndpoint,
ProviderTypes,
} from '@ew-did-registry/did-resolver-interface';
import { VerifiableCredential } from '@ew-did-registry/credentials-interface';
import { ClaimManager__factory } from '../../../ethers/factories/ClaimManager__factory';
import { ERROR_MESSAGES } from '../../errors';
import { emptyAddress } from '../../utils/constants';
Expand Down Expand Up @@ -64,7 +71,10 @@ import { compareDID, isValidDID } from '../../utils/did';
import { JWT } from '@ew-did-registry/jwt';
import { privToPem, KeyType } from '@ew-did-registry/keys';
import { readyToBeRegisteredOnchain } from './claims.types';
import { VerifiableCredentialsServiceBase } from '../verifiable-credentials';
import {
RoleCredentialSubject,
VerifiableCredentialsServiceBase,
} from '../verifiable-credentials';
import { NotAuthorizedIssuer } from '../../errors/not-authorized-issuer';

const {
Expand All @@ -90,6 +100,7 @@ const {
export class ClaimsService {
private _claimManager: string;
private _claimManagerInterface = ClaimManager__factory.createInterface();
private _vcIssuerVerifier: VCIssuerVerification;
private _claimRevocation: ClaimRevocation;

constructor(
Expand All @@ -100,6 +111,7 @@ export class ClaimsService {
private _verifiableCredentialService: VerifiableCredentialsServiceBase
) {
this._signerService.onInit(this.init.bind(this));
this._setClaimIssuerVerifier();
}

static async create(
Expand Down Expand Up @@ -374,8 +386,6 @@ export class ClaimsService {
claimData: { claimType: string; claimTypeVersion: number };
sub: string;
};
await this.verifyIssuer(claimData.claimType);

await this.verifyEnrolmentPrerequisites({
subject: sub,
role: claimData.claimType,
Expand Down Expand Up @@ -415,6 +425,7 @@ export class ClaimsService {
}

if (registrationTypes.includes(RegistrationTypes.OffChain)) {
await this.verifyVcIssuer(claimData.claimType);
const publicClaim: IPublicClaim = {
did: sub,
signer: this._signerService.did,
Expand All @@ -441,6 +452,19 @@ export class ClaimsService {
await this._cacheClient.issueClaim(this._signerService.did, message);
}

async verifyVc(vc: VerifiableCredential<RoleCredentialSubject>) {
const issuerDID = this._signerService.did;
const role = vc.credentialSubject.role.namespace;
if (
!(
(await this._vcIssuerVerifier.verifyIssuerAuthority(role, issuerDID)) ||
(await this._vcIssuerVerifier.verifyChainOfTrustByRoleDefinition(vc))
)
) {
throw new NotAuthorizedIssuer(issuerDID, role);
}
}

/**
* Register issued on-chain claim on Claim Manager contract.
*
Expand Down Expand Up @@ -572,7 +596,7 @@ export class ClaimsService {
registrationTypes = [RegistrationTypes.OffChain],
claim,
}: IssueClaimOptions): Promise<string | undefined> {
await this.verifyIssuer(claim.claimType);
await this.verifyVcIssuer(claim.claimType);
await this.verifyEnrolmentPrerequisites({ subject, role: claim.claimType });

const message: IClaimIssuance = {
Expand Down Expand Up @@ -1114,15 +1138,16 @@ export class ClaimsService {
}

/**
* Verify if the user is able to issue the given role. Throws an error when the user is not able to issue the given role.
* Verify if the user is issuer of the role verifiable credential
*
* @param {String} role Registration types of the claim
*/
private async verifyIssuer(role: string): Promise<void> {
private async verifyVcIssuer(role: string): Promise<void> {
if (
!(
await this._cacheClient.getAllowedRolesByIssuer(this._signerService.did)
).some((r) => r.namespace === role)
!(await this._vcIssuerVerifier.verifyIssuerAuthority(
role,
this._signerService.did
))
) {
throw new NotAuthorizedIssuer(this._signerService.did, role);
}
Expand Down Expand Up @@ -1312,4 +1337,22 @@ export class ClaimsService {
await this._signerService.signMessage(arrayify(proofHash))
);
}

private _setClaimIssuerVerifier() {
const credentialResolver: CredentialResolver = new IpfsCredentialResolver(
this._signerService.provider,
this._didRegistry.registrySettings,
this._didRegistry.ipfsStore
);
const issuerResolver = new EthersProviderIssuerDefinitionResolver(
this._signerService.provider,
chainConfigs()[this._signerService.chainId].ensResolverV2Address
);
this._vcIssuerVerifier = new VCIssuerVerification(
this._signerService.provider,
this._didRegistry.registrySettings,
credentialResolver,
issuerResolver
);
}
}
7 changes: 7 additions & 0 deletions src/modules/did-registry/did-registry.service.ts
Expand Up @@ -18,6 +18,7 @@ import {
KeyTags,
ProviderTypes,
PubKeyType,
RegistrySettings,
} from '@ew-did-registry/did-resolver-interface';
import {
DIDDocumentFull,
Expand Down Expand Up @@ -226,6 +227,12 @@ export class DidRegistry {
return didDocument?.delegates;
}

get registrySettings(): RegistrySettings {
return {
address: chainConfigs()[this._signerService.chainId].didRegistryAddress,
};
}

/**
* Create a public claim with provided data.
*
Expand Down

0 comments on commit b318dbd

Please sign in to comment.