Source Code

;; access-audit - Clarity 4
;; Comprehensive audit trail for genomic data access

(define-constant ERR-NOT-AUTHORIZED (err u100))
(define-constant ERR-AUDIT-NOT-FOUND (err u101))
(define-constant ERR-INVALID-ACTION (err u102))
(define-constant ERR-RESOURCE-NOT-FOUND (err u103))

(define-constant ACTION-READ "read")
(define-constant ACTION-WRITE "write")
(define-constant ACTION-DELETE "delete")
(define-constant ACTION-SHARE "share")
(define-constant ACTION-EXPORT "export")

(define-map audit-logs uint
  {
    resource-id: uint,
    resource-type: (string-ascii 50),
    accessor: principal,
    action: (string-ascii 20),
    timestamp: uint,
    success: bool,
    ip-hash: (buff 32),
    metadata: (string-utf8 200)
  }
)

(define-map access-stats { resource-id: uint, accessor: principal }
  {
    total-accesses: uint,
    last-access: uint,
    failed-attempts: uint
  }
)

(define-map resource-owners uint
  {
    owner: principal,
    created-at: uint,
    audit-enabled: bool
  }
)

(define-map suspicious-activity principal
  {
    failed-login-attempts: uint,
    last-failed-attempt: uint,
    is-blocked: bool,
    blocked-until: uint
  }
)

(define-data-var audit-counter uint u0)
(define-data-var max-failed-attempts uint u5)
(define-data-var block-duration uint u86400) ;; 24 hours

;; Log access attempt
(define-public (log-access
    (resource-id uint)
    (resource-type (string-ascii 50))
    (action (string-ascii 20))
    (success bool)
    (ip-hash (buff 32))
    (metadata (string-utf8 200)))
  (let ((audit-id (+ (var-get audit-counter) u1)))
    (map-set audit-logs audit-id
      {
        resource-id: resource-id,
        resource-type: resource-type,
        accessor: tx-sender,
        action: action,
        timestamp: stacks-block-time,
        success: success,
        ip-hash: ip-hash,
        metadata: metadata
      })
    (update-access-stats resource-id tx-sender success)
    (if (not success)
      (track-failed-attempt tx-sender)
      true)
    (var-set audit-counter audit-id)
    (ok audit-id)))

;; Register resource for auditing
(define-public (register-resource (resource-id uint))
  (begin
    (asserts! (is-none (map-get? resource-owners resource-id)) (err u104))
    (ok (map-set resource-owners resource-id
      {
        owner: tx-sender,
        created-at: stacks-block-time,
        audit-enabled: true
      }))))

;; Enable/disable auditing
(define-public (toggle-auditing (resource-id uint) (enabled bool))
  (let ((resource (unwrap! (map-get? resource-owners resource-id) ERR-RESOURCE-NOT-FOUND)))
    (asserts! (is-eq tx-sender (get owner resource)) ERR-NOT-AUTHORIZED)
    (ok (map-set resource-owners resource-id (merge resource { audit-enabled: enabled })))))

;; Update access statistics
(define-private (update-access-stats (resource-id uint) (accessor principal) (success bool))
  (let ((stats (default-to
                 { total-accesses: u0, last-access: u0, failed-attempts: u0 }
                 (map-get? access-stats { resource-id: resource-id, accessor: accessor }))))
    (map-set access-stats { resource-id: resource-id, accessor: accessor }
      {
        total-accesses: (+ (get total-accesses stats) u1),
        last-access: stacks-block-time,
        failed-attempts: (if success
                           (get failed-attempts stats)
                           (+ (get failed-attempts stats) u1))
      })
    true))

;; Track failed login attempts
(define-private (track-failed-attempt (accessor principal))
  (let ((activity (default-to
                    { failed-login-attempts: u0, last-failed-attempt: u0, is-blocked: false, blocked-until: u0 }
                    (map-get? suspicious-activity accessor))))
    (let ((new-attempts (+ (get failed-login-attempts activity) u1)))
      (map-set suspicious-activity accessor
        {
          failed-login-attempts: new-attempts,
          last-failed-attempt: stacks-block-time,
          is-blocked: (>= new-attempts (var-get max-failed-attempts)),
          blocked-until: (if (>= new-attempts (var-get max-failed-attempts))
                           (+ stacks-block-time (var-get block-duration))
                           (get blocked-until activity))
        })
      true)))

;; Clear failed attempts after successful login
(define-public (clear-failed-attempts (accessor principal))
  (ok (map-delete suspicious-activity accessor)))

;; Read-only functions
(define-read-only (get-audit-log (audit-id uint))
  (ok (map-get? audit-logs audit-id)))

(define-read-only (get-access-stats (resource-id uint) (accessor principal))
  (ok (map-get? access-stats { resource-id: resource-id, accessor: accessor })))

(define-read-only (get-resource-info (resource-id uint))
  (ok (map-get? resource-owners resource-id)))

(define-read-only (get-suspicious-activity (accessor principal))
  (ok (map-get? suspicious-activity accessor)))

(define-read-only (is-accessor-blocked (accessor principal))
  (let ((activity (map-get? suspicious-activity accessor)))
    (ok (match activity
          record (and (get is-blocked record) (< stacks-block-time (get blocked-until record)))
          false))))

(define-read-only (get-total-audit-logs)
  (ok (var-get audit-counter)))

;; Clarity 4: principal-destruct?
(define-read-only (validate-accessor (accessor principal))
  (principal-destruct? accessor))

;; Clarity 4: int-to-ascii
(define-read-only (format-audit-id (audit-id uint))
  (ok (int-to-ascii audit-id)))

;; Clarity 4: string-to-uint?
(define-read-only (parse-audit-id (id-str (string-ascii 20)))
  (string-to-uint? id-str))

;; Clarity 4: burn-block-height
(define-read-only (get-bitcoin-block)
  (ok burn-block-height))

Functions (16)

FunctionAccessArgs
log-accesspublicresource-id: uint, resource-type: (string-ascii 50
register-resourcepublicresource-id: uint
toggle-auditingpublicresource-id: uint, enabled: bool
update-access-statsprivateresource-id: uint, accessor: principal, success: bool
track-failed-attemptprivateaccessor: principal
clear-failed-attemptspublicaccessor: principal
get-audit-logread-onlyaudit-id: uint
get-access-statsread-onlyresource-id: uint, accessor: principal
get-resource-inforead-onlyresource-id: uint
get-suspicious-activityread-onlyaccessor: principal
is-accessor-blockedread-onlyaccessor: principal
get-total-audit-logsread-only
validate-accessorread-onlyaccessor: principal
format-audit-idread-onlyaudit-id: uint
parse-audit-idread-onlyid-str: (string-ascii 20
get-bitcoin-blockread-only