Skip to content

Commit

Permalink
Add support for dropdown audit fields.
Browse files Browse the repository at this point in the history
  • Loading branch information
kezike committed May 13, 2024
1 parent e2454f9 commit 7638fdc
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 11 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# opencred-platform Changelog

## 7.1.0 - 2024-05-xx

### Added
- Add support for dropdown audit fields.

## 7.0.0 - 2024-04-30

### Changed
Expand Down
34 changes: 29 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,17 +279,41 @@ auditFields:
name: Date of Birth
path: "$.credentialSubject.birth_date"
required: true
- type: number
id: height
name: Height (cm)
path: "$.credentialSubject.height"
required: false
- type: dropdown
id: sex
name: Sex
path: "$.credentialSubject.sex"
required: false
options:
"Male": 1
"Female": 2
- type: dropdown
id: senior_citizen
name: Are you a senior citizen?
path: "$.credentialSubject.senior_citizen"
required: true
options:
"Yes": 1
"No": null
```

The `enableAudit` field enables support for auditing in an OpenCred deployment.
If you would also like to check for matching values in the token's credential
in a web interface, you can specify the following attributes for each
field of interest via the `auditFields` field and visit `BASE_URL/audit-vp` in the browser:
- `type` - the field type (currently, supports `text`, `number`, and `date`)
- `id` - the field ID (can be anything, but must be unique among other fields)
- `name` - the field name that appears in the web interface
- `path` - the field path in the credential (must be unique among other fields)
- `required` - whether the admin user is required to enter a value for the field in the web interface
- `type` - The field type (currently, supports `text`, `number`, `date`, and `dropdown`).
- `id` - The field ID (can be anything, but must be unique among other fields).
- `name` - The field name that appears in the web interface.
- `path` - The field path in the credential (must be unique among other fields).
- `required` - Whether the admin user is required to enter a value for the field in the web interface.
- `options` - Data binding from user-friendly name to associated value for the field in the web interface. This property is used whenever a field can have one of multiple possible machine-readable values in a discrete set of options (e.g., `Male` -> `1`, `Female` -> `2`). The input for this field will be presented as a dropdown selection element. If one of the options is the absence of the field from the credential, you can represent this by binding the field to `null`. For example, here are the expectations for each selection for the field named `Are you a senior citizen?` in the sample snippet above:
- `Yes` - There exists a field with path `$.credentialSubject.senior_citizen` containing value `1` in the credential.
- `No` - There does **not** exist a field with path `$.credentialSubject.senior_citizen` in the credential.

If you want to test out the audit feature, follow these steps:
1. Run an instance of OpenCred using the instructions below.
Expand Down
5 changes: 4 additions & 1 deletion common/audit.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,10 @@ const getFieldMatchesDiVp = (vpToken, fields) => {
Object.entries(fields)
.map(([path, value]) => {
const credentialMatches = credentials.some(c => {
const [credentialValue] = jp.query(c, path);
let [credentialValue] = jp.query(c, path);
credentialValue = credentialValue === undefined ?
null :
credentialValue;
return value === credentialValue;
});
return [path, credentialMatches];
Expand Down
21 changes: 21 additions & 0 deletions configs/config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,27 @@ app:
name: Date of Birth
path: "$.credentialSubject.birth_date"
required: true
- type: number
id: height
name: Height (cm)
path: "$.credentialSubject.height"
required: false
- type: dropdown
id: sex
name: Sex
path: "$.credentialSubject.sex"
required: false
options:
"Male": 1
"Female": 2
- type: dropdown
id: senior_citizen
name: Are you a senior citizen?
path: "$.credentialSubject.senior_citizen"
required: true
options:
"Yes": 1
"No": null
defaultLanguage: en
translations:
en:
Expand Down
6 changes: 4 additions & 2 deletions configs/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -351,11 +351,13 @@ bedrock.events.on('bedrock.init', async () => {
/**
* A field to audit in a VP token
* @typedef {Object} AuditField
* @property {'text' | 'number' | 'date'} type - Type of audit field.
* @property {'text' | 'number' | 'date' | 'dropdown'} type
* - Type of audit field.
* @property {string} id - Unique ID of audit field.
* @property {string} name - Name of audit field.
* @property {string} path - Path of audit field in the VP token.
* @property {string} required - Whether audit field is required.
* @property {string} options - Options for dropdown fields.
*/

/**
Expand All @@ -364,7 +366,7 @@ bedrock.events.on('bedrock.init', async () => {
*/

const requiredAuditFieldKeys = ['type', 'id', 'name', 'path', 'required'];
const auditFieldTypes = ['text', 'number', 'date'];
const auditFieldTypes = ['text', 'number', 'date', 'dropdown'];
const validateAuditFields = () => {
if(!opencred.auditFields) {
return;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@
"tailwindcss": "^3.4.1",
"unhead": "^1.9.7",
"vue": "^3.3.4",
"vue-i18n": "^9.9.0",
"vue-cookies": "^1.8.4",
"vue-i18n": "^9.9.0",
"vue-json-pretty": "^2.3.0",
"vue-material-design-icons": "^5.3.0",
"vue-router": "^4.3.0",
Expand Down
24 changes: 22 additions & 2 deletions web/components/AuditPresentation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ import {config} from '@bedrock/web';
import {httpClient} from '@digitalbazaar/http-client';
import {ref} from 'vue';

const NON_STANDARD_TYPES = ['dropdown'];

const auditFieldValues = ref(
Object.fromEntries(
config.auditFields
.map(f => [f.path, null])
.map(f => [f.path, undefined])
)
);

Expand Down Expand Up @@ -169,16 +171,34 @@ function clearAuditResults() {
'row mb-5'">
<label
:for="[field.id]"
class="col-2 font-md font-bold mr-3">
class="col-3 font-md font-bold mr-3">
{{field.name}}
</label>
<input
v-if="!NON_STANDARD_TYPES.includes(field.type)"
:id="field.id"
v-model="auditFieldValues[field.path]"
:type="field.type"
class="col-6 font-md border rounded px-2 py-2 mr-5"
:placeholder="field.required ? 'Required' : 'Optional'"
:required="field.required">
<select
v-else-if="field.type === 'dropdown'"
v-model="auditFieldValues[field.path]"
class="col-6 font-md border rounded px-2 py-2 mr-5"
:required="field.required">
<option
disabled
value="">
Please select one
</option>
<option
v-for="option in Object.entries(field.options)"
:key="option[0]"
:value="option[1]">
{{option[0]}}
</option>
</select>
<div
v-if="Object.entries(auditResults.data.matches).length > 0 &&
!auditResults.loading"
Expand Down

0 comments on commit 7638fdc

Please sign in to comment.