Medical Record Access Control
What it does:
Controls who can access a patient’s medical records by enforcing on-chain permissions, access scopes, and time-based authorization without exposing actual medical data.
Why it matters:
Prevents unauthorized access, data misuse, and silent breaches by making all medical record access permissioned, traceable, and revocable by the patient.
How it works:
-
Medical records are represented by hashes or off-chain references (e.g. IPFS)
-
Patients are the sole owners of access permissions
-
Healthcare providers request access to specific records
-
Patients grant access with scope and expiration
-
Access can be revoked instantly at any time
-
All access permissions are transparently auditable on-chain
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title MedicalRecordAccessControl
* @author Nam
* @notice On-chain access control for medical record references
*/
contract MedicalRecordAccessControl {
// -------------------- RECORD STRUCT --------------------
struct MedicalRecord {
string recordHash; // IPFS hash or encrypted data reference
bool exists;
}
struct AccessPermission {
bool allowed;
uint256 expiry;
}
// -------------------- STORAGE --------------------
// patient => recordId => MedicalRecord
mapping(address => mapping(uint256 => MedicalRecord)) public records;
// patient => recordId => provider => AccessPermission
mapping(address => mapping(uint256 => mapping(address => AccessPermission))) public permissions;
// -------------------- EVENTS --------------------
event RecordAdded(address indexed patient, uint256 indexed recordId);
event AccessGranted(
address indexed patient,
uint256 indexed recordId,
address indexed provider,
uint256 expiry
);
event AccessRevoked(
address indexed patient,
uint256 indexed recordId,
address indexed provider
);
// -------------------- MODIFIERS --------------------
modifier onlyPatient(address _patient) {
require(msg.sender == _patient, "Not patient");
_;
}
// -------------------- RECORD MANAGEMENT --------------------
/**
* @notice Add a medical record reference
*/
function addRecord(
uint256 _recordId,
string calldata _recordHash
)
external
onlyPatient(msg.sender)
{
require(!records[msg.sender][_recordId].exists, "Record already exists");
records[msg.sender][_recordId] = MedicalRecord({
recordHash: _recordHash,
exists: true
});
emit RecordAdded(msg.sender, _recordId);
}
// -------------------- ACCESS CONTROL --------------------
/**
* @notice Grant access to a healthcare provider
*/
function grantAccess(
uint256 _recordId,
address _provider,
uint256 _duration
)
external
onlyPatient(msg.sender)
{
require(records[msg.sender][_recordId].exists, "Record not found");
require(_provider != address(0), "Invalid provider");
require(_duration > 0, "Invalid duration");
permissions[msg.sender][_recordId][_provider] = AccessPermission({
allowed: true,
expiry: block.timestamp + _duration
});
emit AccessGranted(
msg.sender,
_recordId,
_provider,
block.timestamp + _duration
);
}
/**
* @notice Revoke provider access
*/
function revokeAccess(
uint256 _recordId,
address _provider
)
external
onlyPatient(msg.sender)
{
AccessPermission storage permission =
permissions[msg.sender][_recordId][_provider];
require(permission.allowed, "Access not granted");
permission.allowed = false;
permission.expiry = 0;
emit AccessRevoked(msg.sender, _recordId, _provider);
}
// -------------------- VIEW FUNCTIONS --------------------
/**
* @notice Check if provider has access to a medical record
*/
function hasAccess(
address _patient,
uint256 _recordId,
address _provider
)
external
view
returns (bool)
{
AccessPermission memory permission =
permissions[_patient][_recordId][_provider];
return permission.allowed && block.timestamp <= permission.expiry;
}
/**
* @notice Get medical record reference (only metadata, not raw data)
*/
function getRecordHash(
address _patient,
uint256 _recordId
)
external
view
returns (string memory)
{
require(
permissions[_patient][_recordId][msg.sender].allowed &&
block.timestamp <= permissions[_patient][_recordId][msg.sender].expiry,
"Access denied"
);
return records[_patient][_recordId].recordHash;
}
}