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;
    }
}