Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions project-data-room-consent-ledger/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Project Data Room Consent Ledger

This module adds a User and Project Management slice for project data-room access, object-level permissions, and export consent. It is self-contained, dependency-free, and synthetic-data-only so reviewers can validate it without accounts, SAML credentials, ORCID tokens, or a running platform.

It covers issue #11 by evaluating:

- institutional, external, and anonymous-review identity evidence
- MFA, ORCID, SAML, and identity-escrow requirements before access is granted
- project visibility and external collaborator sponsor requirements
- role-based and object-level permissions for documents, datasets, and review threads
- restricted dataset download consent with IRB, data-use agreement, and export policy evidence
- immutable audit-chain and export-packet digests for reviewer and institutional records

This is not another broad RBAC demo, offboarding workflow, identity merge, or anonymous-review escrow implementation. The focus is the handoff point where project managers must prove that a collaborator may enter a research data room and export a restricted object.

## Local Validation

```sh
node project-data-room-consent-ledger/test.js
node project-data-room-consent-ledger/demo.js
```

## Demo Evidence

- [demo.mp4](demo.mp4) shows the problem, implementation scope, access decisions, and validation commands.
- [demo.svg](demo.svg) provides a static reviewer dashboard preview.
- [requirements-map.md](requirements-map.md) maps the implementation to issue #11.
- [acceptance-notes.md](acceptance-notes.md) lists the reviewer checks.
11 changes: 11 additions & 0 deletions project-data-room-consent-ledger/acceptance-notes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Acceptance Notes

Reviewer checks:

1. Run `node project-data-room-consent-ledger/test.js`.
2. Run `node project-data-room-consent-ledger/demo.js`.
3. Confirm the valid scenario approves two grants, verifies both identities, and emits SHA-256 audit/export digests.
4. Confirm the broken scenario holds unsafe access when MFA, ORCID, expiry, sponsor, role permission, and restricted-data consent evidence are missing.
5. Confirm anonymous review identity displays the pseudonym while still requiring escrow evidence.

The module uses only Node built-ins and synthetic inputs. It does not call live identity providers, inspect user secrets, or store real participant data.
103 changes: 103 additions & 0 deletions project-data-room-consent-ledger/demo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
"use strict";

const { evaluateProjectDataRoom } = require("./index");

const room = {
generatedAt: "2026-05-17T12:00:00.000Z",
identities: [
{
id: "pi-morgan",
name: "Dr. Morgan",
affiliationType: "institutional",
links: ["email", "saml", "orcid", "github"],
mfaVerified: true,
profileMode: "public",
},
{
id: "external-biostat",
name: "Dr. Patel",
affiliationType: "external",
links: ["email", "orcid"],
mfaVerified: true,
trainingExpiresAt: "2026-10-30",
profileMode: "private",
},
{
id: "blind-reviewer",
mode: "anonymous-review",
pseudonym: "Reviewer B",
affiliationType: "external",
links: ["email", "orcid", "anonymousProfile", "identityEscrow"],
mfaVerified: true,
},
],
projects: [
{
id: "project-metabolomics",
title: "Metabolomics cohort workspace",
visibility: "institutional-only",
fundingSource: "Foundation cohort grant",
},
],
objects: [
{ id: "draft-paper", projectId: "project-metabolomics", kind: "manuscript", sensitivity: "internal" },
{ id: "cohort-table", projectId: "project-metabolomics", kind: "dataset", sensitivity: "restricted" },
{ id: "review-thread", projectId: "project-metabolomics", kind: "discussion", sensitivity: "internal" },
],
consentRecords: [
{
id: "consent-biostat-export",
irbProtocol: "IRB-2026-077",
dataUseAgreement: "DUA-METAB-2026",
exportPolicy: "aggregate-results-and-model-coefficients",
},
],
grants: [
{
id: "grant-pi-admin",
identityId: "pi-morgan",
projectId: "project-metabolomics",
objectId: "draft-paper",
role: "admin",
actions: ["read", "comment", "edit", "share"],
},
{
id: "grant-biostat-data-room",
identityId: "external-biostat",
projectId: "project-metabolomics",
objectId: "cohort-table",
role: "admin",
actions: ["read", "download"],
consentId: "consent-biostat-export",
expiresAt: "2026-06-17",
institutionalSponsor: "pi-morgan",
},
{
id: "grant-anonymous-review",
identityId: "blind-reviewer",
projectId: "project-metabolomics",
objectId: "review-thread",
role: "reviewer",
actions: ["read", "comment"],
expiresAt: "2026-05-31",
institutionalSponsor: "pi-morgan",
},
],
auditEvents: [
{ type: "workspace-created", actorId: "pi-morgan", targetId: "project-metabolomics" },
{ type: "consent-attached", actorId: "pi-morgan", targetId: "consent-biostat-export" },
{ type: "grant-approved", actorId: "pi-morgan", targetId: "grant-biostat-data-room" },
{ type: "anonymous-review-opened", actorId: "pi-morgan", targetId: "grant-anonymous-review" },
],
};

const result = evaluateProjectDataRoom(room);

console.log("Project data room consent ledger demo");
console.log(JSON.stringify(result.dashboard, null, 2));
console.log("Grant decisions:");
for (const grant of result.grants) {
console.log(`- ${grant.id}: ${grant.decision} (${grant.role}, ${grant.actions.join(", ")})`);
}
console.log("Export packet:");
console.log(JSON.stringify(result.exportPacket, null, 2));
Binary file added project-data-room-consent-ledger/demo.mp4
Binary file not shown.
38 changes: 38 additions & 0 deletions project-data-room-consent-ledger/demo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading