diff --git a/api-docs/package.json b/api-docs/package.json index 21131a5c1a..274d0ff494 100644 --- a/api-docs/package.json +++ b/api-docs/package.json @@ -1,6 +1,6 @@ { "name": "api-docs", - "version": "2.2.0", + "version": "2.2.1", "description": "Swagger Documentation", "main": "dist/index.js", "scripts": { diff --git a/api-gateway/package.json b/api-gateway/package.json index 7568b181e7..5b8fb6a7ff 100644 --- a/api-gateway/package.json +++ b/api-gateway/package.json @@ -8,8 +8,8 @@ }, "author": "Envision Blockchain Solutions ", "dependencies": { - "@guardian/common": "^2.2.0", - "@guardian/interfaces": "^2.2.0", + "@guardian/common": "^2.2.1", + "@guardian/interfaces": "^2.2.1", "dotenv": "^16.0.0", "express": "^4.17.1", "jszip": "^3.7.1", @@ -49,5 +49,5 @@ "start": "node dist/index.js", "test": "mocha tests/**/*.test.js --reporter mocha-junit-reporter --reporter-options mochaFile=../test_results/ui-service.xml" }, - "version": "2.2.0" + "version": "2.2.1" } diff --git a/api-gateway/src/api/service/tokens.ts b/api-gateway/src/api/service/tokens.ts index b2f1a6eafb..c4992897da 100644 --- a/api-gateway/src/api/service/tokens.ts +++ b/api-gateway/src/api/service/tokens.ts @@ -49,11 +49,18 @@ tokenAPI.post('/', permissionHelper(UserRole.STANDARD_REGISTRY), async (req: Aut res.status(500).json({ code: 500, message: 'User not registered' }); return; } - const tokens = (await guardians.setToken({ + + let tokens = (await guardians.setToken({ token: req.body, owner: user.did })); + tokens = await guardians.getTokens({ + did: user.did + }); + + tokens = tokens || []; + await setTokensPolicies(tokens, user); res.status(201).json(tokens); } catch (error) { diff --git a/api-gateway/src/api/service/websockets.ts b/api-gateway/src/api/service/websockets.ts index 0bc50f145f..5817ab0985 100644 --- a/api-gateway/src/api/service/websockets.ts +++ b/api-gateway/src/api/service/websockets.ts @@ -134,7 +134,7 @@ export class WebSocketsService { this.channel.response('block-error', async (msg) => { this.wss.clients.forEach((client: any) => { try { - if (client.user.did === msg.user.did) { + if (client.user && msg.user && client.user.did === msg.user.did) { client.send(JSON.stringify({ type: 'error-event', data: { @@ -154,7 +154,7 @@ export class WebSocketsService { console.log('update-user-info'); this.wss.clients.forEach((client: any) => { try { - if (client.user.did === msg.user.did) { + if (client.user && msg.user && client.user.did === msg.user.did) { client.send(JSON.stringify({ type: 'update-user-info-event', data: msg @@ -205,11 +205,7 @@ export class WebSocketsService { this.channel.response('update-user-balance', async (msg) => { this.wss.clients.forEach((client: any) => { try { - if ( - client.user && - msg.user && - client.user.username === msg.user.username - ) { + if (client.user && msg.user && client.user.username === msg.user.username) { client.send(JSON.stringify({ type: 'PROFILE_BALANCE', data: msg diff --git a/auth-service/package.json b/auth-service/package.json index 46745166f6..e8020cbe4d 100644 --- a/auth-service/package.json +++ b/auth-service/package.json @@ -7,8 +7,8 @@ }, "author": "Envision Blockchain Solutions ", "dependencies": { - "@guardian/common": "^2.2.0", - "@guardian/interfaces": "^2.2.0", + "@guardian/common": "^2.2.1", + "@guardian/interfaces": "^2.2.1", "dotenv": "^16.0.0", "jsonwebtoken": "^8.5.1", "module-alias": "^2.2.2", @@ -44,5 +44,5 @@ "start": "node dist/index.js", "test": "mocha tests/**/*.test.js --reporter mocha-junit-reporter --reporter-options mochaFile=../test_results/ui-service.xml" }, - "version": "2.2.0" + "version": "2.2.1" } diff --git a/common/package.json b/common/package.json index 8090599b62..9bfd8839c8 100644 --- a/common/package.json +++ b/common/package.json @@ -1,7 +1,7 @@ { "author": "Envision Blockchain Solutions ", "dependencies": { - "@guardian/interfaces": "^2.2.0", + "@guardian/interfaces": "^2.2.1", "nats": "^2.6.1", "reflect-metadata": "^0.1.13", "zlib": "^1.0.5" @@ -27,5 +27,5 @@ "prepare": "npm run build", "test": "echo \"Error: no test specified\" && exit 1" }, - "version": "2.2.0" + "version": "2.2.1" } diff --git a/docs/.gitbook/assets/image (13) (1).png b/docs/.gitbook/assets/image (13) (1).png new file mode 100644 index 0000000000..9192379a75 Binary files /dev/null and b/docs/.gitbook/assets/image (13) (1).png differ diff --git a/docs/.gitbook/assets/image (13).png b/docs/.gitbook/assets/image (13).png index 9192379a75..e4ee206a55 100644 Binary files a/docs/.gitbook/assets/image (13).png and b/docs/.gitbook/assets/image (13).png differ diff --git a/docs/.gitbook/assets/image (23).png b/docs/.gitbook/assets/image (23).png new file mode 100644 index 0000000000..705b73c00f Binary files /dev/null and b/docs/.gitbook/assets/image (23).png differ diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index d16513c807..2d1c33c833 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -75,6 +75,7 @@ * [revokeBlock](available-policy-workflow-blocks/revokeblock.md) * [setRelationshipsBlock](available-policy-workflow-blocks/setrelationshipsblock.md) * [buttonBlock](available-policy-workflow-blocks/buttonblock.md) +* [documentValidatorBlock](available-policy-workflow-blocks/documentvalidatorblock.md) * [Events](available-policy-workflow-blocks/events.md) ## Policy Workflow Creation using the Guardian User Interface diff --git a/docs/available-policy-workflow-blocks/documentvalidatorblock.md b/docs/available-policy-workflow-blocks/documentvalidatorblock.md new file mode 100644 index 0000000000..31235ed36a --- /dev/null +++ b/docs/available-policy-workflow-blocks/documentvalidatorblock.md @@ -0,0 +1,52 @@ +# documentValidatorBlock + +This Block is to validate documents, including linked documents. This block returns an error if at least one of the checks don’t pass. It can be placed as a ‘child’ document, or as a link in the sequence of the blocks. + +![](<../.gitbook/assets/image (13).png>) + +### Properties + +| Block Property | Definition | Example Input | Status | +| --------------------- | --------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | ------ | +| tag | Unique name for the logic block. | documentValidatorBlock | | +| permissions | Which entity has rights to interact at this part of the workflow. | VVB | | +| defaultActive | Shows whether this block is active at this time and whether it needs to be shown. | Checked or unchecked. | | +| On errors | Called if the system error has occurs in the Block |

  • No action
  • Retry
  • Go to step
  • Go to tag
| | +| stop Propagation | End processing here, don't pass control to the next block. | Checked or unchecked. | | +| DocumentType | Type of the documents to be validated. |

· VC Document

· VP Document

· Related VC
Document

. Related VP Document

| | +| Check Schema | Validates schema documents. | Schema | | +| Check Own Document | If ‘true’ validates document owners. | True / False | | +| Check Assign Document | If ‘true’ validates document owners. | True / False | | +| Conditions | Array containing conditions for validation. | Array | | + + + +![](<../.gitbook/assets/image (23).png>) + +### Document Type + +The following document types are supported: + +1. VC Document : validates the main document and its type (Verifiable Credential). +2. VP Document : validates the main document and its type (Verifiable Presentation). +3. Related VC Document : validates the document linked to the main document and its type (Verifiable Credential) +4. Related VP Document : validates the document linked to the main document and its type (Verifiable Presentation) + +### Conditions + +#### Condition N + +#### Type: + +1. Equal – resolves to ‘true’ if value of the field is equal the the content of the Value parameter. +2. Not Equal – resolves to ‘true’ if value of the field is NOT equal the the content of the Value parameter. +3. In – resolves to ‘true’ if value of the field is present the the array. +4. Not In – resolves to ‘true’ if value of the field is present the the array. + +#### Field : + +This field of the document to validates the Value parameter. + +#### Value: + +The content of this parameter is compared to the content of the Field. diff --git a/docs/available-policy-workflow-blocks/introduction.md b/docs/available-policy-workflow-blocks/introduction.md index 1906b1f286..616d8b863a 100644 --- a/docs/available-policy-workflow-blocks/introduction.md +++ b/docs/available-policy-workflow-blocks/introduction.md @@ -31,3 +31,5 @@ Starting with the [Wikipedia definition](https://en.wikipedia.org/wiki/Workflow\ | revokeBlock | This Block finds related messages in policy topics, and revokes those messages and sends it to Hedera topic, but it doesn’t save documents in DB | [revokeblock.md](revokeblock.md "mention") | | setRelationshipsBlock | Contains DocumentSourceAddOn Block and set relationships for input document. | [setrelationshipsblock.md](setrelationshipsblock.md "mention") | | buttonBlock | Block responsible for creating buttons | [buttonblock.md](buttonblock.md "mention") | +| documentValidatorBlock | Block to validate documents, including linked documents | [documentvalidatorblock.md](documentvalidatorblock.md "mention") | + diff --git a/docs/schema-creation-using-the-guardian-apis/updating-schema.md b/docs/schema-creation-using-the-guardian-apis/updating-schema.md index 3149d46c3e..b6e7ebe357 100644 --- a/docs/schema-creation-using-the-guardian-apis/updating-schema.md +++ b/docs/schema-creation-using-the-guardian-apis/updating-schema.md @@ -1,10 +1,10 @@ -# Updating schema +# Updating Schema ### UPDATING SCHEMA BASED ON SCHEMA ID -{% swagger method="get" path="" baseUrl="/schemas/{schemaId}" summary="Updates the schema" %} +{% swagger method="put" path="" baseUrl="/schemas/{schemaId}" summary="Updates the schema" %} {% swagger-description %} -Updates the schema with the provided schema ID. Only users with the Standard Registry role are allowed to make the request. +Updates the schema matching the id in the request body. Only users with the Standard Registry role are allowed to make the request. {% endswagger-description %} {% swagger-parameter in="path" name="schemaID" type="String" required="true" %} @@ -12,7 +12,7 @@ Schema ID {% endswagger-parameter %} {% swagger-parameter in="body" type="schema" required="true" %} -Object that contains a valid schema +Object that contains a valid schema including the id of the schema that is to be update {% endswagger-parameter %} {% swagger-response status="200: OK" description="Succesful Operation" %} diff --git a/docs/schema-flow/schema-demo.md b/docs/schema-flow/schema-demo.md index 2ac2186948..2210a659f2 100644 --- a/docs/schema-flow/schema-demo.md +++ b/docs/schema-flow/schema-demo.md @@ -13,7 +13,7 @@ To display System / Policy Schemas in the GUI, we have added a toggle in the Sch Whenever an account is created, System Schemas are generated automatically. -![](<../.gitbook/assets/image (13).png>) +![](<../.gitbook/assets/image (13) (1).png>) {% hint style="info" %} Note: By default System Schemas cannot be edited / deleted. diff --git a/frontend/package.json b/frontend/package.json index cb56c42a3c..7300b4159e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -57,5 +57,5 @@ "test": "ng test", "watch": "ng build --watch --configuration development --output-path ../www-data" }, - "version": "2.2.0" + "version": "2.2.1" } diff --git a/frontend/src/app/policy-engine/helpers/preview-policy-dialog/preview-policy-dialog.component.css b/frontend/src/app/policy-engine/helpers/preview-policy-dialog/preview-policy-dialog.component.css index a4e0111280..c2ad9c5679 100644 --- a/frontend/src/app/policy-engine/helpers/preview-policy-dialog/preview-policy-dialog.component.css +++ b/frontend/src/app/policy-engine/helpers/preview-policy-dialog/preview-policy-dialog.component.css @@ -59,13 +59,22 @@ form { color: #000; font-size: 20px; margin-bottom: 20px; - max-width: 600px; + max-width: 720px; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; margin-left: 8px; } +.field-long-value { + color: #000; + font-size: 20px; + margin-bottom: 20px; + max-width: 720px; + overflow: hidden; + margin-left: 8px; +} + .field-name { margin-bottom: 12px; } diff --git a/frontend/src/app/policy-engine/helpers/preview-policy-dialog/preview-policy-dialog.component.html b/frontend/src/app/policy-engine/helpers/preview-policy-dialog/preview-policy-dialog.component.html index 010f37fcdf..d3fbead25c 100644 --- a/frontend/src/app/policy-engine/helpers/preview-policy-dialog/preview-policy-dialog.component.html +++ b/frontend/src/app/policy-engine/helpers/preview-policy-dialog/preview-policy-dialog.component.html @@ -50,7 +50,7 @@
Creator
-
{{policy.owner}}
+
{{policy.owner}}
@@ -64,7 +64,7 @@
Schemas Overview
-
{{schemas}}
+
{{schemas}}
diff --git a/frontend/src/app/policy-engine/policy-configuration/blocks/documents/document-validator-config/document-validator-config.component.css b/frontend/src/app/policy-engine/policy-configuration/blocks/documents/document-validator-config/document-validator-config.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frontend/src/app/policy-engine/policy-configuration/blocks/documents/document-validator-config/document-validator-config.component.html b/frontend/src/app/policy-engine/policy-configuration/blocks/documents/document-validator-config/document-validator-config.component.html new file mode 100644 index 0000000000..4ba1dbfe30 --- /dev/null +++ b/frontend/src/app/policy-engine/policy-configuration/blocks/documents/document-validator-config/document-validator-config.component.html @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Document Type + + VC Document + VP Document + Related VC Document + Related VP Document + +
Check Schema + + + {{schema.name}} + ({{schema.version}}) + ({{schema.status}}) + + +
Check Owner Document + +
Check Assign Document + +
+ + expand_more + + Conditions +
+ add + Add Condition +
+
+ + expand_more + + Condition {{i}} + + {{condition.field}} {{condition.type}} {{condition.value}} + +
Type + + Equal + Not Equal + In + Not In + +
Field + +
Value + +
\ No newline at end of file diff --git a/frontend/src/app/policy-engine/policy-configuration/blocks/documents/document-validator-config/document-validator-config.component.ts b/frontend/src/app/policy-engine/policy-configuration/blocks/documents/document-validator-config/document-validator-config.component.ts new file mode 100644 index 0000000000..9779b6ea58 --- /dev/null +++ b/frontend/src/app/policy-engine/policy-configuration/blocks/documents/document-validator-config/document-validator-config.component.ts @@ -0,0 +1,61 @@ +import { Component, EventEmitter, Input, OnInit, Output, SimpleChanges } from '@angular/core'; +import { Schema, Token } from '@guardian/interfaces'; +import { PolicyBlockModel, PolicyModel } from 'src/app/policy-engine/policy-model'; +import { BlockNode } from '../../../../helpers/tree-data-source/tree-data-source'; + +/** + * Settings for block of 'sendToGuardian' type. + */ +@Component({ + selector: 'document-validator-config', + templateUrl: './document-validator-config.component.html', + styleUrls: [ + './../../../common-properties/common-properties.component.css', + './document-validator-config.component.css' + ] +}) +export class DocumentValidatorConfigComponent implements OnInit { + @Input('policy') policy!: PolicyModel; + @Input('block') currentBlock!: PolicyBlockModel; + @Input('schemas') schemas!: Schema[]; + @Input('tokens') tokens!: Token[]; + @Input('readonly') readonly!: boolean; + @Output() onInit = new EventEmitter(); + + propHidden: any = { + main: false, + conditionsGroup: false, + conditions: {}, + }; + + block!: any; + + constructor() { + } + + ngOnInit(): void { + this.onInit.emit(this); + this.load(this.currentBlock); + } + + ngOnChanges(changes: SimpleChanges) { + this.load(this.currentBlock); + } + + load(block: PolicyBlockModel) { + this.block = block.properties; + this.block.conditions = this.block.conditions || []; + } + + onHide(item: any, prop: any) { + item[prop] = !item[prop]; + } + + addCondition() { + this.block.conditions.push({ + value: '', + field: '', + type: 'equal', + }) + } +} diff --git a/frontend/src/app/policy-engine/policy-engine.module.ts b/frontend/src/app/policy-engine/policy-engine.module.ts index 71f894eb2c..a21efbd8da 100644 --- a/frontend/src/app/policy-engine/policy-engine.module.ts +++ b/frontend/src/app/policy-engine/policy-engine.module.ts @@ -58,6 +58,7 @@ import { RevokeConfigComponent } from './policy-configuration/blocks/documents/r import { EventsOverview } from './helpers/events-overview/events-overview'; import { ButtonConfigComponent } from './policy-configuration/blocks/main/button-config/button-config.component'; import { ButtonBlockComponent } from './policy-viewer/blocks/button-block/button-block.component'; +import { DocumentValidatorConfigComponent } from './policy-configuration/blocks/documents/document-validator-config/document-validator-config.component'; @NgModule({ declarations: [ @@ -110,7 +111,8 @@ import { ButtonBlockComponent } from './policy-viewer/blocks/button-block/button ConfirmationDialog, RevokeConfigComponent, ButtonConfigComponent, - ButtonBlockComponent + ButtonBlockComponent, + DocumentValidatorConfigComponent ], imports: [ CommonModule, @@ -164,7 +166,8 @@ import { ButtonBlockComponent } from './policy-viewer/blocks/button-block/button JsonPropertiesComponent, ReassigningConfigComponent, CronConfigDialog, - EventsOverview + EventsOverview, + DocumentValidatorConfigComponent ], providers: [ RegisteredBlocks diff --git a/frontend/src/app/policy-engine/policy-viewer/blocks/documents-source-block/documents-source-block.component.css b/frontend/src/app/policy-engine/policy-viewer/blocks/documents-source-block/documents-source-block.component.css index 7f66aa93d7..3d40323cd1 100644 --- a/frontend/src/app/policy-engine/policy-viewer/blocks/documents-source-block/documents-source-block.component.css +++ b/frontend/src/app/policy-engine/policy-viewer/blocks/documents-source-block/documents-source-block.component.css @@ -180,4 +180,9 @@ tr.revoked td:not(.mat-column-history):before { left: 0; border-bottom: 2px solid rgb(105, 105, 105); width: 100%; + transform: translate(0px, -4px); +} + +tr.revoked td:not(.mat-column-history) { + opacity: 0.7; } \ No newline at end of file diff --git a/frontend/src/app/policy-engine/policy-viewer/blocks/documents-source-block/documents-source-block.component.ts b/frontend/src/app/policy-engine/policy-viewer/blocks/documents-source-block/documents-source-block.component.ts index 4716930c23..ba9b506409 100644 --- a/frontend/src/app/policy-engine/policy-viewer/blocks/documents-source-block/documents-source-block.component.ts +++ b/frontend/src/app/policy-engine/policy-viewer/blocks/documents-source-block/documents-source-block.component.ts @@ -102,7 +102,6 @@ export class DocumentsSourceBlockComponent implements OnInit { async setData(data: any) { if (data) { - const fields: any[] = data.fields || []; this.fieldMap = {}; this.fields = []; @@ -147,7 +146,9 @@ export class DocumentsSourceBlockComponent implements OnInit { return new Promise(async (resolve, reject) => { this.policyEngineService.getGetIdByName(element.bindBlock, this.policyId).subscribe(({ id }: any) => { this.policyEngineService.getBlockData(id, this.policyId).subscribe((data: any) => { - data.id = id; + if(data) { + data.id = id; + } resolve(data); }, (e) => { reject(); diff --git a/frontend/src/app/policy-engine/policy-viewer/blocks/report-block/report-block.component.html b/frontend/src/app/policy-engine/policy-viewer/blocks/report-block/report-block.component.html index d364535f1c..dfd92141a7 100644 --- a/frontend/src/app/policy-engine/policy-viewer/blocks/report-block/report-block.component.html +++ b/frontend/src/app/policy-engine/policy-viewer/blocks/report-block/report-block.component.html @@ -93,11 +93,11 @@
Verified Signature:
-
{{vpDocument.document.document.proof.jws}}
+
{{vpDocument.document.document.proof.jws}}
Verified Signature:
-
{{vcDocument.document.document.proof.jws}}
+
{{vcDocument.document.document.proof.jws}}
@@ -148,6 +148,7 @@ chevron_right +
diff --git a/frontend/src/app/policy-engine/registered-blocks.ts b/frontend/src/app/policy-engine/registered-blocks.ts index ba9a3d40bf..4c176aa041 100644 --- a/frontend/src/app/policy-engine/registered-blocks.ts +++ b/frontend/src/app/policy-engine/registered-blocks.ts @@ -34,6 +34,7 @@ import { RevokeConfigComponent } from "./policy-configuration/blocks/documents/r import { ButtonConfigComponent } from "./policy-configuration/blocks/main/button-config/button-config.component"; import { ButtonBlockComponent } from "./policy-viewer/blocks/button-block/button-block.component"; import { GenerateUUIDv4 } from '@guardian/interfaces'; +import { DocumentValidatorConfigComponent } from "./policy-configuration/blocks/documents/document-validator-config/document-validator-config.component"; export enum BlockType { Container = 'interfaceContainerBlock', @@ -61,7 +62,8 @@ export enum BlockType { Switch = 'switchBlock', RevokeBlock = 'revokeBlock', SetRelationshipsBlock = 'setRelationshipsBlock', - ButtonBlock = 'buttonBlock' + ButtonBlock = 'buttonBlock', + DocumentValidatorBlock = 'documentValidatorBlock' } export enum BlockGroup { @@ -274,7 +276,8 @@ export class RegisteredBlocks { { type: BlockType.Report }, { type: BlockType.RevokeBlock }, { type: BlockType.SetRelationshipsBlock }, - { type: BlockType.ButtonBlock } + { type: BlockType.ButtonBlock }, + { type: BlockType.DocumentValidatorBlock } ]; // Main, UI Components @@ -401,7 +404,7 @@ export class RegisteredBlocks { allowedChildren: [{ type: BlockType.FiltersAddon, group: BlockGroup.UnGrouped - },{ + }, { type: BlockType.DocumentsSourceAddon, group: BlockGroup.UnGrouped }, { @@ -419,6 +422,9 @@ export class RegisteredBlocks { allowedChildren: [{ type: BlockType.DocumentsSourceAddon, group: BlockGroup.UnGrouped + }, { + type: BlockType.DocumentValidatorBlock, + group: BlockGroup.UnGrouped }] }); @@ -438,6 +444,10 @@ export class RegisteredBlocks { header: BlockHeaders.ServerBlocks, factory: null, property: ExternalDataConfigComponent, + allowedChildren: [{ + type: BlockType.DocumentValidatorBlock, + group: BlockGroup.UnGrouped + }] }); this.registerBlock({ type: BlockType.AggregateDocument, @@ -470,12 +480,10 @@ export class RegisteredBlocks { header: BlockHeaders.ServerBlocks, factory: null, property: null, - allowedChildren: [ - { - type: BlockType.DocumentsSourceAddon, - group: BlockGroup.UnGrouped - } - ] + allowedChildren: [{ + type: BlockType.DocumentsSourceAddon, + group: BlockGroup.UnGrouped + }] }); // Documents, Addons @@ -519,6 +527,14 @@ export class RegisteredBlocks { factory: null, property: TimerConfigComponent, }); + this.registerBlock({ + type: BlockType.DocumentValidatorBlock, + icon: 'task_alt', + group: BlockGroup.Documents, + header: BlockHeaders.Addons, + factory: null, + property: DocumentValidatorConfigComponent + }); // Tokens, Server Blocks this.registerBlock({ diff --git a/frontend/src/app/views/user-profile/user-profile.component.ts b/frontend/src/app/views/user-profile/user-profile.component.ts index 6fac12ce5e..78f4ca08af 100644 --- a/frontend/src/app/views/user-profile/user-profile.component.ts +++ b/frontend/src/app/views/user-profile/user-profile.component.ts @@ -114,6 +114,7 @@ export class UserProfileComponent implements OnInit { } }); this.standardRegistries = value[3] || []; + this.standardRegistries = this.standardRegistries.filter(sr => !!sr.did); this.isConfirmed = !!this.profile.confirmed; this.isFailed = !!this.profile.failed; diff --git a/guardian-service/package.json b/guardian-service/package.json index cf4fbdf395..0dea4b88d8 100644 --- a/guardian-service/package.json +++ b/guardian-service/package.json @@ -11,8 +11,8 @@ }, "author": "Envision Blockchain Solutions ", "dependencies": { - "@guardian/common": "^2.2.0", - "@guardian/interfaces": "^2.2.0", + "@guardian/common": "^2.2.1", + "@guardian/interfaces": "^2.2.1", "@hashgraph/sdk": "^2.15.0", "@transmute/credentials-context": "^0.7.0-unstable.60", "@transmute/did-context": "^0.7.0-unstable.60", @@ -72,5 +72,5 @@ "test:network": "mocha tests/network-tests/**/*.test.js", "test:stability": "mocha tests/stability.test.js" }, - "version": "2.2.0" + "version": "2.2.1" } diff --git a/guardian-service/src/helpers/utils.ts b/guardian-service/src/helpers/utils.ts index 3f100dc066..56f2ef195c 100644 --- a/guardian-service/src/helpers/utils.ts +++ b/guardian-service/src/helpers/utils.ts @@ -95,17 +95,12 @@ export function findOptions(document: any, field: any) { value = document; for (let i = 0; i < keys.length; i++) { const key = keys[i]; - if (Array.isArray(value[key])) { - value = value[key].join(','); - } else { - value = value[key]; - } + value = value[key]; } } return value; } - export function replaceValueRecursive(document: any, replaceMap: Map): any { let str: string; switch (typeof document) { diff --git a/guardian-service/src/policy-engine/blocks/document-validator-block.ts b/guardian-service/src/policy-engine/blocks/document-validator-block.ts new file mode 100644 index 0000000000..e902cc0170 --- /dev/null +++ b/guardian-service/src/policy-engine/blocks/document-validator-block.ts @@ -0,0 +1,185 @@ +import { IAuthUser } from '@auth/auth.interface'; +import { BlockActionError } from '@policy-engine/errors'; +import { ActionCallback, BasicBlock, ValidatorBlock } from '@policy-engine/helpers/decorators'; +import { CatchErrors } from '@policy-engine/helpers/decorators/catch-errors'; +import { IPolicyEvent, PolicyInputEventType, PolicyOutputEventType } from '@policy-engine/interfaces'; +import { ChildrenType, ControlType } from '@policy-engine/interfaces/block-about'; +import { IPolicyBlock, IPolicyValidatorBlock } from '@policy-engine/policy-engine.interface'; +import { PolicyValidationResultsContainer } from '@policy-engine/policy-validation-results-container'; +import { PolicyComponentsUtils } from '../policy-components-utils'; +import { Schema as SchemaCollection } from '@entity/schema'; +import { PolicyUtils } from '@policy-engine/helpers/utils'; +import { getMongoRepository } from 'typeorm'; +import { VcDocument as VcDocumentCollection } from '@entity/vc-document'; +import { VpDocument as VpDocumentCollection } from '@entity/vp-document'; + +@ValidatorBlock({ + blockType: 'documentValidatorBlock', + commonBlock: false, + about: { + label: 'Validator', + title: `Add 'Validator' Block`, + post: false, + get: false, + children: ChildrenType.None, + control: ControlType.Special, + input: [ + PolicyInputEventType.RunEvent + ], + output: [ + PolicyOutputEventType.RunEvent, + PolicyOutputEventType.RefreshEvent + ], + defaultEvent: true + } +}) +export class DocumentValidatorBlock { + public async run(event: IPolicyEvent): Promise { + const ref = PolicyComponentsUtils.GetBlockRef(this); + + let document = event?.data?.data; + + if (!document) { + return false; + } + + const documentRef = PolicyUtils.getDocumentRef(document); + + if (ref.options.documentType === 'related-vc-document') { + if (documentRef) { + document = await getMongoRepository(VcDocumentCollection).findOne({ + where: { + 'policyId': { $eq: ref.policyId }, + 'document.credentialSubject.id': { $eq: documentRef } + } + }); + } else { + document = null; + } + } + + if (ref.options.documentType === 'related-vp-document') { + if (documentRef) { + document = await getMongoRepository(VpDocumentCollection).findOne({ + where: { + 'policyId': ref.policyId, + 'document.verifiableCredential.credentialSubject.id': { $eq: documentRef } + } + }); + } else { + document = null; + } + } + + if (!document) { + return false; + } + + const documentType = PolicyUtils.getDocumentType(document); + + if (ref.options.documentType === 'vc-document') { + if (documentType !== 'VerifiableCredential') { + return false; + } + } else if (ref.options.documentType === 'vp-document') { + if (documentType !== 'VerifiablePresentation') { + return false; + } + } else if (ref.options.documentType === 'related-vc-document') { + if (documentType !== 'VerifiableCredential') { + return false; + } + } else if (ref.options.documentType === 'related-vp-document') { + if (documentType !== 'VerifiablePresentation') { + return false; + } + } + + if (ref.options.checkOwnerDocument) { + if (document.owner !== event?.user?.did) { + return false; + } + } + + if (ref.options.checkAssignDocument) { + if (document.assign !== event?.user?.did) { + return false; + } + } + + if (ref.options.schema) { + const schema = await getMongoRepository(SchemaCollection).findOne({ + iri: ref.options.schema, + topicId: ref.topicId + }); + if (!PolicyUtils.checkDocumentSchema(document, schema)) { + return false; + } + } + + if (ref.options.conditions) { + for (let filter of ref.options.conditions) { + if (!PolicyUtils.checkDocumentField(document, filter)) { + return false; + } + } + } + + return true; + } + + /** + * @event PolicyEventType.Run + * @param {IPolicyEvent} event + */ + @ActionCallback({ + output: [PolicyOutputEventType.RunEvent, PolicyOutputEventType.RefreshEvent] + }) + async runAction(event: IPolicyEvent) { + const ref = PolicyComponentsUtils.GetBlockRef(this); + ref.log(`runAction`); + + if (!ref.run(event)) { + throw new BlockActionError(`Invalid document`, ref.blockType, ref.uuid); + } + + ref.triggerEvents(PolicyOutputEventType.RunEvent, event.user, event.data); + ref.triggerEvents(PolicyOutputEventType.RefreshEvent, event.user, event.data); + } + + public async validate(resultsContainer: PolicyValidationResultsContainer): Promise { + const ref = PolicyComponentsUtils.GetBlockRef(this); + try { + const types = [ + 'vc-document', + 'vp-document', + 'related-vc-document', + 'related-vp-document' + ]; + if (types.indexOf(ref.options.documentType) == -1) { + resultsContainer.addBlockError(ref.uuid, 'Option "documentType" must be one of ' + types.join(',')); + } + + if (ref.options.schema) { + if (typeof ref.options.schema !== 'string') { + resultsContainer.addBlockError(ref.uuid, 'Option "schema" must be a string'); + return; + } + const schema = await getMongoRepository(SchemaCollection).findOne({ + iri: ref.options.schema, + topicId: ref.topicId + }); + if (!schema) { + resultsContainer.addBlockError(ref.uuid, `Schema with id "${ref.options.schema}" does not exist`); + return; + } + } + + if (ref.options.conditions && !Array.isArray(ref.options.conditions)) { + resultsContainer.addBlockError(ref.uuid, `conditions option must be an array`); + } + } catch (error) { + resultsContainer.addBlockError(ref.uuid, `Unhandled exception ${error.message}`); + } + } +} diff --git a/guardian-service/src/policy-engine/blocks/external-data-block.ts b/guardian-service/src/policy-engine/blocks/external-data-block.ts index 10984753b6..8b451a14f2 100644 --- a/guardian-service/src/policy-engine/blocks/external-data-block.ts +++ b/guardian-service/src/policy-engine/blocks/external-data-block.ts @@ -9,6 +9,9 @@ import { Schema as SchemaCollection } from '@entity/schema'; import { CatchErrors } from '@policy-engine/helpers/decorators/catch-errors'; import { PolicyOutputEventType } from '@policy-engine/interfaces'; import { ChildrenType, ControlType } from '@policy-engine/interfaces/block-about'; +import { IPolicyValidatorBlock } from '@policy-engine/policy-engine.interface'; +import { IAuthUser } from '@auth/auth.interface'; +import { BlockActionError } from '@policy-engine/errors'; /** * External data block @@ -32,6 +35,38 @@ import { ChildrenType, ControlType } from '@policy-engine/interfaces/block-about } }) export class ExternalDataBlock { + protected getValidators(): IPolicyValidatorBlock[] { + const ref = PolicyComponentsUtils.GetBlockRef(this); + const validators: IPolicyValidatorBlock[] = []; + for (let child of ref.children) { + if (child.blockClassName === 'ValidatorBlock') { + validators.push(child as IPolicyValidatorBlock); + } + } + return validators; + } + + protected async validateDocuments(user: IAuthUser, state: any): Promise { + const validators = this.getValidators(); + for (const validator of validators) { + const valid = await validator.run({ + type: null, + inputType: null, + outputType: null, + policyId: null, + source: null, + sourceId: null, + target: null, + targetId: null, + user: user, + data: state + }); + if (!valid) { + return false; + } + } + return true; + } @ActionCallback({ output: [PolicyOutputEventType.RunEvent, PolicyOutputEventType.RefreshEvent] @@ -65,6 +100,12 @@ export class ExternalDataBlock { schema: ref.options.schema }; const state = { data: doc }; + + const valid = await this.validateDocuments(null, state); + if (!valid) { + throw new BlockActionError('Invalid document', ref.blockType, ref.uuid); + } + ref.triggerEvents(PolicyOutputEventType.RunEvent, null, state); ref.triggerEvents(PolicyOutputEventType.RefreshEvent, null, state); } diff --git a/guardian-service/src/policy-engine/blocks/index.ts b/guardian-service/src/policy-engine/blocks/index.ts index 1833d74748..ad3fc83b5f 100644 --- a/guardian-service/src/policy-engine/blocks/index.ts +++ b/guardian-service/src/policy-engine/blocks/index.ts @@ -25,4 +25,5 @@ export { SwitchBlock } from './switch-block'; export { TimerBlock } from './timer-block'; export { RevokeBlock } from './revoke-block'; export { SetRelationshipsBlock } from './set-relationships-block'; -export { ButtonBlock } from './button-block'; \ No newline at end of file +export { ButtonBlock } from './button-block'; +export { DocumentValidatorBlock } from './document-validator-block'; \ No newline at end of file diff --git a/guardian-service/src/policy-engine/blocks/policy-roles.ts b/guardian-service/src/policy-engine/blocks/policy-roles.ts index 19fcedc020..cc54c2c412 100644 --- a/guardian-service/src/policy-engine/blocks/policy-roles.ts +++ b/guardian-service/src/policy-engine/blocks/policy-roles.ts @@ -35,24 +35,15 @@ export class PolicyRolesBlock { } async setData(user: IAuthUser, document: any): Promise { - const policyRepository = getMongoRepository(Policy); const ref = PolicyComponentsUtils.GetBlockRef(this); - const currentPolicy = await policyRepository.findOne(ref.policyId); + const { username, role, did } = user; + const result = await PolicyComponentsUtils.SetUserRole(ref.policyId, user, document.role); - if (typeof currentPolicy.registeredUsers !== 'object') { - currentPolicy.registeredUsers = {}; - } - currentPolicy.registeredUsers[user.did] = document.role; - - const {username, role, did} = user; - - const result = await policyRepository.save(currentPolicy); await Promise.all([ - PolicyComponentsUtils.BlockUpdateFn(ref.parent.uuid, {}, {username, role, did}, ref.tag), - PolicyComponentsUtils.UpdateUserInfoFn({username, role, did}, currentPolicy) + PolicyComponentsUtils.BlockUpdateFn(ref.parent.uuid, {}, { username, role, did }, ref.tag), + PolicyComponentsUtils.UpdateUserInfoFn({ username, role, did }, result) ]); - return result; } } diff --git a/guardian-service/src/policy-engine/blocks/report-item-block.ts b/guardian-service/src/policy-engine/blocks/report-item-block.ts index 4337fe961b..866d29d34d 100644 --- a/guardian-service/src/policy-engine/blocks/report-item-block.ts +++ b/guardian-service/src/policy-engine/blocks/report-item-block.ts @@ -53,7 +53,7 @@ export class ReportItemBlock { } resultFields.push(item); - const filtersToVc = {}; + const filtersToVc:any = {}; if (ref.options.filters) { for (let index = 0; index < ref.options.filters.length; index++) { const filter = ref.options.filters[index]; @@ -65,19 +65,28 @@ export class ReportItemBlock { } switch (filter.type) { case 'equal': - expr = {$eq: expr}; + expr = { $eq: expr }; break; case 'not_equal': - expr = {$ne: expr}; + expr = { $ne: expr }; break; case 'in': - expr = {$in: expr.split(',')}; + if (Array.isArray(expr)) { + expr = { $in: expr }; + } else if (expr) { + expr = { $in: [expr] }; + } break; case 'not_in': - expr = {$nin: expr.split(',')}; + if (Array.isArray(expr)) { + expr = { $in: expr }; + } else if (expr) { + expr = { $in: [expr] }; + } + expr = { $nin: expr }; break; default: @@ -86,6 +95,7 @@ export class ReportItemBlock { filtersToVc[filter.field] = expr; } } + filtersToVc.policyId = { $eq: ref.policyId }; const vcDocument = await getMongoRepository(VcDocument).findOne(filtersToVc); diff --git a/guardian-service/src/policy-engine/blocks/request-vc-document-block.ts b/guardian-service/src/policy-engine/blocks/request-vc-document-block.ts index c3071fd1bf..243ae5eb09 100644 --- a/guardian-service/src/policy-engine/blocks/request-vc-document-block.ts +++ b/guardian-service/src/policy-engine/blocks/request-vc-document-block.ts @@ -12,10 +12,11 @@ import { VcHelper } from '@helpers/vcHelper'; import { getMongoRepository } from 'typeorm'; import { Schema as SchemaCollection } from '@entity/schema'; import { DidDocument as DidDocumentCollection } from '@entity/did-document'; -import { IPolicyRequestBlock } from '@policy-engine/policy-engine.interface'; +import { IPolicyRequestBlock, IPolicyValidatorBlock } from '@policy-engine/policy-engine.interface'; import { PolicyUtils } from '@policy-engine/helpers/utils'; import { PolicyInputEventType, PolicyOutputEventType } from '@policy-engine/interfaces'; import { ChildrenType, ControlType } from '@policy-engine/interfaces/block-about'; +import { VcDocument as VcDocumentCollection } from '@entity/vc-document'; @EventBlock({ blockType: 'requestVcDocumentBlock', @@ -50,6 +51,39 @@ export class RequestVcDocumentBlock { constructor() { } + protected getValidators(): IPolicyValidatorBlock[] { + const ref = PolicyComponentsUtils.GetBlockRef(this); + const validators: IPolicyValidatorBlock[] = []; + for (let child of ref.children) { + if (child.blockClassName === 'ValidatorBlock') { + validators.push(child as IPolicyValidatorBlock); + } + } + return validators; + } + + protected async validateDocuments(user: IAuthUser, state: any): Promise { + const validators = this.getValidators(); + for (const validator of validators) { + const valid = await validator.run({ + type: null, + inputType: null, + outputType: null, + policyId: null, + source: null, + sourceId: null, + target: null, + targetId: null, + user: user, + data: state + }); + if (!valid) { + return false; + } + } + return true; + } + @ActionCallback({ output: PolicyOutputEventType.RefreshEvent }) @@ -112,6 +146,39 @@ export class RequestVcDocumentBlock { }; } + async getRelationships(policyId: string, refId: any): Promise { + if (refId) { + let id: string = null; + let documentRef: any = null; + if (typeof (refId) === 'string') { + documentRef = await getMongoRepository(VcDocumentCollection).findOne({ + where: { + 'policyId': { $eq: policyId }, + 'document.credentialSubject.id': { $eq: id } + } + }); + id = refId; + return documentRef; + } else if (typeof (refId) === 'object') { + if (refId.id) { + documentRef = await getMongoRepository(VcDocumentCollection).findOne(refId.id); + } else { + documentRef = await getMongoRepository(VcDocumentCollection).findOne({ + where: { + 'policyId': { $eq: policyId }, + 'document.credentialSubject.id': { $eq: id } + } + }); + } + id = PolicyUtils.getSubjectId(documentRef); + return documentRef; + } + } else { + return null; + } + return null; + } + @ActionCallback({ output: [PolicyOutputEventType.RunEvent, PolicyOutputEventType.RefreshEvent] }) @@ -135,7 +202,7 @@ export class RequestVcDocumentBlock { const userHederaKey = await this.wallet.getKey(user.walletToken, KeyType.KEY, user.did); const document = _data.document; - const documentRef = _data.ref; + const documentRef = await this.getRelationships(ref.policyId, _data.ref); const credentialSubject = document; const schema = ref.options.schema; @@ -143,14 +210,12 @@ export class RequestVcDocumentBlock { const id = await this.generateId(idType, user, userHederaAccount, userHederaKey); const VCHelper = new VcHelper(); + if (id) { credentialSubject.id = id; } - if (typeof (documentRef) === 'string') { - credentialSubject.ref = documentRef; - } - else if (documentRef) { + if (documentRef) { credentialSubject.ref = PolicyUtils.getSubjectId(documentRef); } @@ -171,12 +236,18 @@ export class RequestVcDocumentBlock { relationships: null }; - if (typeof (documentRef) === 'object' && documentRef && documentRef.messageId) { + if (documentRef) { item.relationships = [documentRef.messageId]; } - await this.changeActive(user, true); const state = { data: item }; + + const valid = await this.validateDocuments(user, state); + if (!valid) { + throw new BlockActionError('Invalid document', ref.blockType, ref.uuid); + } + + await this.changeActive(user, true); ref.triggerEvents(PolicyOutputEventType.RunEvent, user, state); ref.triggerEvents(PolicyOutputEventType.RefreshEvent, user, state); } catch (error) { @@ -246,7 +317,7 @@ export class RequestVcDocumentBlock { resultsContainer.addBlockError(ref.uuid, 'Option "schema" must be a string'); return; } - const schema = await getMongoRepository(SchemaCollection).findOne({ + const schema = await getMongoRepository(SchemaCollection).findOne({ iri: ref.options.schema, topicId: ref.topicId }); @@ -255,7 +326,7 @@ export class RequestVcDocumentBlock { return; } if (ref.options.presetSchema) { - const presetSchema = await getMongoRepository(SchemaCollection).findOne({ + const presetSchema = await getMongoRepository(SchemaCollection).findOne({ iri: ref.options.presetSchema, topicId: ref.topicId }); diff --git a/guardian-service/src/policy-engine/helpers/decorators/basic-block.ts b/guardian-service/src/policy-engine/helpers/decorators/basic-block.ts index 9d83cb1015..3e57fe0cf7 100644 --- a/guardian-service/src/policy-engine/helpers/decorators/basic-block.ts +++ b/guardian-service/src/policy-engine/helpers/decorators/basic-block.ts @@ -8,12 +8,11 @@ import { PolicyValidationResultsContainer } from '@policy-engine/policy-validati import { IAuthUser } from '@auth/auth.interface'; import { getMongoRepository } from 'typeorm'; import { BlockState } from '@entity/block-state'; -import deepEqual from 'deep-equal'; -import { BlockActionError } from '@policy-engine/errors'; import { Policy } from '@entity/policy'; import { IPolicyEvent, PolicyLink } from '@policy-engine/interfaces/policy-event'; import { PolicyInputEventType, PolicyOutputEventType } from '@policy-engine/interfaces/policy-event-type'; import { ExternalEventChannel, Logger } from '@guardian/common'; +import deepEqual from 'deep-equal'; /** * Basic block decorator @@ -58,19 +57,12 @@ export function BasicBlock(options: Partial) { } } - const o = Object.assign( - options, - PolicyBlockDefaultOptions(), - { - defaultActive: false, - permissions: [] - } - ) as PolicyBlockFullArgumentList; + const defaultOptions = Object.assign(options, PolicyBlockDefaultOptions()) as PolicyBlockFullArgumentList; return class extends basicClass { - static blockType = o.blockType; - static about = o.about; - static publishExternalEvent = o.publishExternalEvent; + static blockType = defaultOptions.blockType; + static about = defaultOptions.about; + static publishExternalEvent = defaultOptions.publishExternalEvent; protected oldDataState: any = {}; protected currentDataState: any = {}; @@ -90,21 +82,20 @@ export function BasicBlock(options: Partial) { constructor( _uuid: string, - defaultActive: boolean, - tag: string, - permissions: PolicyRole[], + _defaultActive: boolean, + _tag: string, + _permissions: PolicyRole[], _parent: IPolicyBlock, _options: any ) { + const tag = _tag || defaultOptions.tag; + const permissions = _permissions || defaultOptions.permissions; + const parent = _parent || defaultOptions._parent; + const active = _defaultActive || defaultOptions.defaultActive || !parent; + super( - o.blockType, - o.commonBlock, - tag || o.tag, - defaultActive || o.defaultActive, - permissions || o.permissions, - _uuid, - _parent || o._parent, - _options + defaultOptions.blockType, defaultOptions.commonBlock, + tag, active, permissions, _uuid, parent, _options ); this.logger = new Logger(); diff --git a/guardian-service/src/policy-engine/helpers/decorators/container-block.ts b/guardian-service/src/policy-engine/helpers/decorators/container-block.ts index a7f6af3e5f..382c01cac0 100644 --- a/guardian-service/src/policy-engine/helpers/decorators/container-block.ts +++ b/guardian-service/src/policy-engine/helpers/decorators/container-block.ts @@ -2,8 +2,6 @@ import { BasicBlock } from '@policy-engine/helpers/decorators/basic-block'; import { PolicyBlockDecoratorOptions } from '@policy-engine/interfaces/block-options'; import { PolicyComponentsUtils } from '../../policy-components-utils'; import { IAuthUser } from '@auth/auth.interface'; -import { getMongoRepository } from 'typeorm'; -import { Policy } from '@entity/policy'; import { IPolicyBlock, IPolicyContainerBlock } from '@policy-engine/policy-engine.interface'; /** @@ -33,34 +31,27 @@ export function ContainerBlock(options: Partial) { } const ref = PolicyComponentsUtils.GetBlockRef(this); - const currentPolicy = await getMongoRepository(Policy).findOne(ref.policyId); - const currentRole = (typeof currentPolicy.registeredUsers === 'object') ? currentPolicy.registeredUsers[user.did] : null; - // const dbUser = await getMongoRepository(User).findOne({ username: user.username }); - - const result = Object.assign(data, { - id: this.uuid, - blockType: this.blockType, - blocks: this.children.map(child => { - let returnValue = { + const currentRole = await PolicyComponentsUtils.GetUserRole(ref.policyId, user); + const children = ref.children.map(child => { + if (child.defaultActive && PolicyComponentsUtils.CheckPermission(child, user, currentRole)) { + return { uiMetaData: child.options.uiMetaData, content: child.blockType, blockType: child.blockType, id: child.uuid, - }; - - if (!child.isActive(user) || !child.defaultActive) { - return undefined; - } - - if (PolicyComponentsUtils.IfHasPermission(child.uuid, currentRole, user)) { - return returnValue; } - + } else { return undefined; - }) + } + }); + + const result = Object.assign(data, { + id: ref.uuid, + blockType: ref.blockType, + blocks: children }) - const changed = (this as any).updateDataState(user, result); + const changed = ref.updateDataState(user, result); return result; } diff --git a/guardian-service/src/policy-engine/helpers/decorators/index.ts b/guardian-service/src/policy-engine/helpers/decorators/index.ts index 3d2ed50611..07097ae2a3 100644 --- a/guardian-service/src/policy-engine/helpers/decorators/index.ts +++ b/guardian-service/src/policy-engine/helpers/decorators/index.ts @@ -10,4 +10,4 @@ export { CalculateBlock } from './calculate-block'; export { Report } from './report-block'; export { ReportItem } from './report-item-block'; export { ActionCallback } from './event-callback'; - +export { ValidatorBlock } from './validator-block'; diff --git a/guardian-service/src/policy-engine/helpers/decorators/validator-block.ts b/guardian-service/src/policy-engine/helpers/decorators/validator-block.ts new file mode 100644 index 0000000000..43179306c1 --- /dev/null +++ b/guardian-service/src/policy-engine/helpers/decorators/validator-block.ts @@ -0,0 +1,26 @@ +import { BasicBlock } from '@policy-engine/helpers/decorators/basic-block'; +import { IPolicyEvent } from '@policy-engine/interfaces'; +import { PolicyBlockDecoratorOptions } from '@policy-engine/interfaces/block-options'; + +/** + * Validator block decorator + * @param options + */ +export function ValidatorBlock(options: Partial) { + return function (constructor: new (...args: any) => any): any { + const basicClass = BasicBlock(options)(constructor); + + return class extends basicClass { + + public readonly blockClassName = 'ValidatorBlock'; + + public async run(event: IPolicyEvent): Promise { + let result: any; + if (typeof super.run === 'function') { + result = super.run(event); + } + return result; + } + } + } +} diff --git a/guardian-service/src/policy-engine/helpers/utils.ts b/guardian-service/src/policy-engine/helpers/utils.ts index c9126fe109..c3c4b6dc5f 100644 --- a/guardian-service/src/policy-engine/helpers/utils.ts +++ b/guardian-service/src/policy-engine/helpers/utils.ts @@ -78,6 +78,21 @@ export class PolicyUtils { return res; } + public static getObjectValue(data: any, field: string): T { + if (field) { + const keys = field.split('.'); + let result = data; + for (const key of keys) { + if (!result) { + return null; + } + result = result[key]; + } + return result; + } + return null; + } + public static getArray(data: T | T[]): T[] { if (Array.isArray(data)) { return data as T[]; @@ -90,7 +105,7 @@ export class PolicyUtils { let item = await getMongoRepository(VcDocumentCollection).findOne({ where: { hash: { $eq: row.hash }, - hederaStatus: {$not: { $eq: DocumentStatus.REVOKE }} + hederaStatus: { $not: { $eq: DocumentStatus.REVOKE } } } }); const docStatusRepo = getMongoRepository(DocumentState); @@ -283,7 +298,7 @@ export class PolicyUtils { public static getSubjectId(data: any): string { try { - if (data) { + if (data && data.document) { if (Array.isArray(data.document.credentialSubject)) { return data.document.credentialSubject[0].id; } else { @@ -313,4 +328,103 @@ export class PolicyUtils { }); return policySchema; } + + public static getDocumentType(document: any): string { + if (document && document.document && document.document.type) { + const type = document.document.type; + if (Array.isArray(type)) { + if (type.indexOf('VerifiableCredential') > -1) { + return 'VerifiableCredential'; + } + if (type.indexOf('VerifiablePresentation') > -1) { + return 'VerifiablePresentation'; + } + } else { + if (type === 'VerifiableCredential') { + return 'VerifiableCredential'; + } + if (type === 'VerifiablePresentation') { + return 'VerifiablePresentation'; + } + } + } + return null; + } + + public static checkDocumentSchema(document: any, schema: SchemaCollection): boolean { + const iri = schema.iri; + const context = schema.contextURL; + if (document && document.document) { + if (Array.isArray(document.document.credentialSubject)) { + return ( + document.document.credentialSubject[0]['@context'].indexOf(context) && + document.document.credentialSubject[0].type == iri + ); + } else { + return ( + document.document.credentialSubject['@context'].indexOf(context) && + document.document.credentialSubject.type == iri + ); + } + } + return true; + } + + public static checkDocumentField(document: any, filter: any): boolean { + if (document) { + const value = PolicyUtils.getObjectValue(document, filter.field); + switch (filter.type) { + case 'equal': + return filter.value === value; + case 'not_equal': + return filter.value !== value; + case 'in': + if (Array.isArray(value)) { + return value.indexOf(filter.value) > -1; + } + return false; + case 'not_in': + if (Array.isArray(value)) { + return value.indexOf(filter.value) === -1; + } + return false; + default: + return false; + } + } + return false; + } + + public static getDocumentRef(document: any) { + let item: any = null; + if (document && document.document) { + if (document.document.credentialSubject) { + const credentialSubject = document.document.credentialSubject; + if (Array.isArray(credentialSubject)) { + item = credentialSubject[0]; + } else { + item = credentialSubject; + } + } else if (document.document.verifiableCredential) { + let vc: any = null; + const verifiableCredential = document.document.verifiableCredential; + if (Array.isArray(verifiableCredential)) { + vc = verifiableCredential[0]; + } else { + vc = verifiableCredential; + } + const credentialSubject = vc.credentialSubject; + if (Array.isArray(credentialSubject)) { + item = credentialSubject[0]; + } else { + item = credentialSubject; + } + } + } + if (item) { + return item.ref; + } else { + return null; + } + } } diff --git a/guardian-service/src/policy-engine/policy-components-utils.ts b/guardian-service/src/policy-engine/policy-components-utils.ts index 2daaae6b22..45c0e9286a 100644 --- a/guardian-service/src/policy-engine/policy-components-utils.ts +++ b/guardian-service/src/policy-engine/policy-components-utils.ts @@ -336,4 +336,72 @@ export class PolicyComponentsUtils { public static GetBlockAbout(): any { return GetBlockAbout(); } + + + public static CheckPermission(block: AnyBlockType, user: IAuthUser, userRole: PolicyRole): boolean { + if (block) { + return (block.isActive(user) && PolicyComponentsUtils.IfHasPermission(block.uuid, userRole, user)); + } else { + return false; + } + } + + public static CheckPermissionTree(block: AnyBlockType, user: IAuthUser, userRole: PolicyRole): boolean { + if (PolicyComponentsUtils.CheckPermission(block, user, userRole)) { + if (block.parent) { + return PolicyComponentsUtils.CheckPermissionTree(block.parent, user, userRole); + } else { + return true; + } + } else { + return false; + } + } + + public static async GetUserRole(policyId: string, user: IAuthUser): Promise { + const currentPolicy = await getMongoRepository(Policy).findOne(policyId); + + if (user && currentPolicy && typeof currentPolicy.registeredUsers === 'object') { + return currentPolicy.registeredUsers[user.did]; + } + + return null; + } + + public static async SetUserRole(policyId: string, user: IAuthUser, role: PolicyRole): Promise { + const currentPolicy = await getMongoRepository(Policy).findOne(policyId); + + if (typeof currentPolicy.registeredUsers !== 'object') { + currentPolicy.registeredUsers = {}; + } + + currentPolicy.registeredUsers[user?.did] = role; + + const result = await getMongoRepository(Policy).save(currentPolicy); + + return result; + } + + public static GetUserRoleList(policy: Policy, did: string): PolicyRole[] { + const userRoles: string[] = []; + if (policy && did) { + if (policy.owner === did) { + userRoles.push('Administrator'); + } + if (policy.registeredUsers && policy.registeredUsers[did]) { + userRoles.push(policy.registeredUsers[did]); + } + } + if (!userRoles.length) { + userRoles.push('The user does not have a role'); + } + return userRoles; + } + + public static GetUserRoleByPolicy(policy: Policy, user: IAuthUser): PolicyRole { + if (user && policy && typeof policy.registeredUsers === 'object') { + return policy.registeredUsers[user.did]; + } + return null; + } } diff --git a/guardian-service/src/policy-engine/policy-engine.interface.ts b/guardian-service/src/policy-engine/policy-engine.interface.ts index c267437eeb..8a02629715 100644 --- a/guardian-service/src/policy-engine/policy-engine.interface.ts +++ b/guardian-service/src/policy-engine/policy-engine.interface.ts @@ -96,6 +96,8 @@ export interface IPolicyBlock { addTargetLink(link: any): void; runAction(event: IPolicyEvent): Promise; + + updateDataState(user: IAuthUser, state: any): boolean; } export interface IPolicyInterfaceBlock extends IPolicyBlock { @@ -187,6 +189,10 @@ export interface IPolicyRequestBlock extends IPolicyBlock { getSources(user: IAuthUser): Promise } +export interface IPolicyValidatorBlock extends IPolicyBlock { + run(event: IPolicyEvent): Promise; +} + export type AnyBlockType = IPolicyBlock | IPolicyInterfaceBlock @@ -195,4 +201,5 @@ export type AnyBlockType = | IPolicyAddonBlock | IPolicyCalculateBlock | IPolicyCalculateAddon - | IPolicyRequestBlock; + | IPolicyRequestBlock + | IPolicyValidatorBlock; diff --git a/guardian-service/src/policy-engine/policy-engine.service.ts b/guardian-service/src/policy-engine/policy-engine.service.ts index 5f6b24dd9e..0c2f126de6 100644 --- a/guardian-service/src/policy-engine/policy-engine.service.ts +++ b/guardian-service/src/policy-engine/policy-engine.service.ts @@ -77,8 +77,7 @@ export class PolicyEngineService { } const block = PolicyComponentsUtils.GetBlockByUUID(uuid) as IPolicyInterfaceBlock; - const policy = await getMongoRepository(Policy).findOne(block.policyId) - const role = policy.registeredUsers[user.did]; + const role = await PolicyComponentsUtils.GetUserRole(block.policyId, user); let changed = true; @@ -120,7 +119,7 @@ export class PolicyEngineService { return; } - const userRole = policy.registeredUsers[user.did]; + const userRole = PolicyComponentsUtils.GetUserRoleByPolicy(policy, user); await this.channel.request(['api-gateway', 'update-user-info'].join('.'), { policyId: policy.id.toString(), @@ -331,23 +330,17 @@ export class PolicyEngineService { this.channel.response(PolicyEngineEvents.GET_POLICY, async (msg) => { const { filters, userDid } = msg; - const data: any = await getMongoRepository(Policy).findOne(filters); - if (data) { - if (userDid) { - data.userRoles = []; - if (data.owner === userDid) { - data.userRoles.push('Administrator'); - } - if (data.registeredUsers && data.registeredUsers[userDid]) { - data.userRoles.push(data.registeredUsers[userDid]); - } - if (!data.userRoles.length) { - data.userRoles.push('The user does not have a role'); - } - } - delete data.registeredUsers; + const policy = await getMongoRepository(Policy).findOne(filters); + + const result: any = policy; + const userRoles: string[] = PolicyComponentsUtils.GetUserRoleList(policy, userDid); + + if (policy) { + result.userRoles = userRoles; + delete result.registeredUsers; } - return new MessageResponse(data); + + return new MessageResponse(result); }); this.channel.response(PolicyEngineEvents.GET_POLICIES, async (msg) => { @@ -362,21 +355,9 @@ export class PolicyEngineService { filter.skip = _pageIndex * _pageSize; } const [policies, count] = await getMongoRepository(Policy).findAndCount(filter); - if (userDid) { - policies.forEach((policy: any) => { - policy.userRoles = []; - if (policy.owner === userDid) { - policy.userRoles.push('Administrator'); - } - if (policy.registeredUsers && policy.registeredUsers[userDid]) { - policy.userRoles.push(policy.registeredUsers[userDid]); - } - if (!policy.userRoles.length) { - policy.userRoles.push('The user does not have a role'); - } - }); - } - policies.forEach(policy => { + + policies.forEach((policy: any) => { + policy.userRoles = PolicyComponentsUtils.GetUserRoleList(policy, userDid); delete policy.registeredUsers; }); @@ -486,10 +467,16 @@ export class PolicyEngineService { this.channel.response(PolicyEngineEvents.POLICY_BLOCKS, async (msg) => { try { - const block = this.policyGenerator.getRoot(msg.policyId); - const user = msg.user; + const { user, policyId } = msg; const userFull = await this.users.getUser(user.username); - return new MessageResponse(await block.getData(userFull, block.uuid)); + const block = this.policyGenerator.getRoot(policyId); + const currentRole = await PolicyComponentsUtils.GetUserRole(policyId, user); + if (PolicyComponentsUtils.CheckPermissionTree(block, user, currentRole)) { + const data = await block.getData(userFull, block.uuid); + return new MessageResponse(data); + } else { + return new MessageResponse(null); + } } catch (error) { new Logger().error(error, ['GUARDIAN_SERVICE']); return new MessageError(error); @@ -500,8 +487,14 @@ export class PolicyEngineService { try { const { user, blockId, policyId } = msg; const userFull = await this.users.getUser(user.username); - const data = await (PolicyComponentsUtils.GetBlockByUUID(blockId) as IPolicyInterfaceBlock).getData(userFull, blockId, null) - return new MessageResponse(data); + const block = PolicyComponentsUtils.GetBlockByUUID(blockId); + const currentRole = await PolicyComponentsUtils.GetUserRole(policyId, user); + if (PolicyComponentsUtils.CheckPermissionTree(block, user, currentRole)) { + const data = await block.getData(userFull, blockId, null); + return new MessageResponse(data); + } else { + return new MessageResponse(null); + } } catch (error) { new Logger().error(error, ['GUARDIAN_SERVICE']); return new MessageError(error); @@ -512,8 +505,14 @@ export class PolicyEngineService { try { const { user, blockId, policyId, data } = msg; const userFull = await this.users.getUser(user.username); - const result = await (PolicyComponentsUtils.GetBlockByUUID(blockId) as IPolicyInterfaceBlock).setData(userFull, data) - return new MessageResponse(result); + const block = PolicyComponentsUtils.GetBlockByUUID(blockId); + const currentRole = await PolicyComponentsUtils.GetUserRole(policyId, user); + if (PolicyComponentsUtils.CheckPermissionTree(block, user, currentRole)) { + const result = await block.setData(userFull, data); + return new MessageResponse(result); + } else { + return new MessageError(new Error('Permission denied')); + } } catch (error) { new Logger().error(error, ['GUARDIAN_SERVICE']); return new MessageError(error); @@ -535,7 +534,7 @@ export class PolicyEngineService { try { const { user, blockId, policyId, data } = msg; const userFull = await this.users.getUser(user.username); - const block = PolicyComponentsUtils.GetBlockByUUID(blockId) as IPolicyInterfaceBlock; + const block = PolicyComponentsUtils.GetBlockByUUID(blockId); let tmpBlock: IPolicyBlock = block; const parents = [block.uuid]; while (tmpBlock.parent) { diff --git a/interfaces/package.json b/interfaces/package.json index c7434b4bf4..ce5d0a03c7 100644 --- a/interfaces/package.json +++ b/interfaces/package.json @@ -24,5 +24,5 @@ "lint": "tslint --project .", "test": "echo \"Error: no test specified\" && exit 1" }, - "version": "2.2.0" + "version": "2.2.1" } diff --git a/ipfs-client/package.json b/ipfs-client/package.json index 00887aaa65..e3549b82a5 100644 --- a/ipfs-client/package.json +++ b/ipfs-client/package.json @@ -4,8 +4,8 @@ }, "author": "Envision Blockchain Solutions ", "dependencies": { - "@guardian/common": "^2.2.0", - "@guardian/interfaces": "^2.2.0", + "@guardian/common": "^2.2.1", + "@guardian/interfaces": "^2.2.1", "@web-std/fetch": "3.0.0", "axios": "^0.26.1", "axios-retry": "^3.2.4", @@ -46,5 +46,5 @@ "start": "node dist/index.js", "test": "mocha tests/**/*.test.js --reporter mocha-junit-reporter --reporter-options mochaFile=../test_results/ipfs-client.xml" }, - "version": "2.2.0" + "version": "2.2.1" } diff --git a/logger-service/package.json b/logger-service/package.json index 4df11a0763..5b262396d5 100644 --- a/logger-service/package.json +++ b/logger-service/package.json @@ -2,8 +2,8 @@ "packageManager": "yarn@3.2.1", "author": "Envision Blockchain Solutions ", "dependencies": { - "@guardian/common": "^2.2.0", - "@guardian/interfaces": "^2.2.0", + "@guardian/common": "^2.2.1", + "@guardian/interfaces": "^2.2.1", "@web-std/fetch": "3.0.0", "cors": "^2.8.5", "dotenv": "^16.0.0", @@ -36,5 +36,5 @@ "lint": "tslint --project .", "start": "node dist/index.js" }, - "version": "2.2.0" + "version": "2.2.1" } diff --git a/mrv-sender/package.json b/mrv-sender/package.json index 94962cb86a..d6635e23c3 100644 --- a/mrv-sender/package.json +++ b/mrv-sender/package.json @@ -1,7 +1,7 @@ { "author": "Envision Blockchain Solutions ", "dependencies": { - "@guardian/common": "^2.2.0", + "@guardian/common": "^2.2.1", "@transmute/credentials-context": "0.7.0-unstable.40", "@transmute/did-context": "0.7.0-unstable.40", "@transmute/ed25519-signature-2018": "0.7.0-unstable.40", @@ -29,5 +29,5 @@ "dev:docker": "nodemon .", "start": "node dist/index.js" }, - "version": "2.2.0" + "version": "2.2.1" } diff --git a/package.json b/package.json index 18dcf031c5..680ac84079 100644 --- a/package.json +++ b/package.json @@ -13,5 +13,5 @@ "logger-service", "mrv-sender" ], - "version": "2.2.0" + "version": "2.2.1" } diff --git a/topic-viewer/package.json b/topic-viewer/package.json index ee4d3d7f88..d1bf5e2104 100644 --- a/topic-viewer/package.json +++ b/topic-viewer/package.json @@ -19,5 +19,5 @@ "dev": "tsc -w", "start": "node dist/index.js" }, - "version": "2.2.0" + "version": "2.2.1" } diff --git a/yarn.lock b/yarn.lock index 85feb64c97..eb850d2328 100644 --- a/yarn.lock +++ b/yarn.lock @@ -169,11 +169,11 @@ __metadata: languageName: node linkType: hard -"@guardian/common@^2.2.0, @guardian/common@workspace:common": +"@guardian/common@^2.2.1, @guardian/common@workspace:common": version: 0.0.0-use.local resolution: "@guardian/common@workspace:common" dependencies: - "@guardian/interfaces": ^2.2.0 + "@guardian/interfaces": ^2.2.1 "@types/node": ^17.0.13 mocha-junit-reporter: ^2.0.2 nats: ^2.6.1 @@ -184,7 +184,7 @@ __metadata: languageName: unknown linkType: soft -"@guardian/interfaces@^2.2.0, @guardian/interfaces@workspace:interfaces": +"@guardian/interfaces@^2.2.1, @guardian/interfaces@workspace:interfaces": version: 0.0.0-use.local resolution: "@guardian/interfaces@workspace:interfaces" dependencies: @@ -1597,8 +1597,8 @@ __metadata: version: 0.0.0-use.local resolution: "api-gateway@workspace:api-gateway" dependencies: - "@guardian/common": ^2.2.0 - "@guardian/interfaces": ^2.2.0 + "@guardian/common": ^2.2.1 + "@guardian/interfaces": ^2.2.1 "@types/express": ^4.17.13 "@types/jszip": ^3.4.1 "@types/node": ^17.0.13 @@ -1769,8 +1769,8 @@ __metadata: version: 0.0.0-use.local resolution: "auth-service@workspace:auth-service" dependencies: - "@guardian/common": ^2.2.0 - "@guardian/interfaces": ^2.2.0 + "@guardian/common": ^2.2.1 + "@guardian/interfaces": ^2.2.1 "@types/jsonwebtoken": ^8.5.4 "@types/node": ^17.0.13 chai: ^4.3.4 @@ -3957,8 +3957,8 @@ __metadata: version: 0.0.0-use.local resolution: "guardian-service@workspace:guardian-service" dependencies: - "@guardian/common": ^2.2.0 - "@guardian/interfaces": ^2.2.0 + "@guardian/common": ^2.2.1 + "@guardian/interfaces": ^2.2.1 "@hashgraph/sdk": ^2.15.0 "@transmute/credentials-context": ^0.7.0-unstable.60 "@transmute/did-context": ^0.7.0-unstable.60 @@ -4527,8 +4527,8 @@ __metadata: version: 0.0.0-use.local resolution: "ipfs-client@workspace:ipfs-client" dependencies: - "@guardian/common": ^2.2.0 - "@guardian/interfaces": ^2.2.0 + "@guardian/common": ^2.2.1 + "@guardian/interfaces": ^2.2.1 "@types/fs-extra": ^9.0.12 "@types/js-yaml": ^4.0.3 "@types/node": ^17.0.13 @@ -5611,8 +5611,8 @@ __metadata: version: 0.0.0-use.local resolution: "logger-service@workspace:logger-service" dependencies: - "@guardian/common": ^2.2.0 - "@guardian/interfaces": ^2.2.0 + "@guardian/common": ^2.2.1 + "@guardian/interfaces": ^2.2.1 "@types/fs-extra": ^9.0.12 "@types/node": ^17.0.13 "@web-std/fetch": 3.0.0 @@ -6144,7 +6144,7 @@ __metadata: version: 0.0.0-use.local resolution: "mrv-sender@workspace:mrv-sender" dependencies: - "@guardian/common": ^2.2.0 + "@guardian/common": ^2.2.1 "@transmute/credentials-context": 0.7.0-unstable.40 "@transmute/did-context": 0.7.0-unstable.40 "@transmute/ed25519-signature-2018": 0.7.0-unstable.40