04/16 Upload codebase
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from sqlalchemy.dialects.mysql import INTEGER
|
||||
|
||||
from app import db
|
||||
|
||||
|
||||
class EmergencyAccess(db.Model):
|
||||
"""
|
||||
Emergency access grant from a vault owner (grantor) to a trusted contact (grantee).
|
||||
|
||||
Status flow:
|
||||
invited → grantee calls /accept → accepted
|
||||
accepted → grantor calls /provide → ready (enc_vault stored)
|
||||
ready → grantee calls /request → pending (wait timer starts)
|
||||
pending → grantor calls /deny → ready (reset, grantee can request again)
|
||||
pending (wait_days elapsed) → grantable (grantee fetches vault)
|
||||
|
||||
Zero-knowledge: enc_vault is a JSON array of vault items re-encrypted by the grantor
|
||||
using the ECDH shared secret (grantor private key + grantee public key).
|
||||
"""
|
||||
__tablename__ = 'emergency_access'
|
||||
|
||||
id = db.Column(INTEGER(unsigned=True), autoincrement=True, primary_key=True)
|
||||
grantor_id = db.Column(
|
||||
INTEGER(unsigned=True),
|
||||
db.ForeignKey('users.id', ondelete='CASCADE'),
|
||||
nullable=False,
|
||||
)
|
||||
grantee_email = db.Column(db.String(255), nullable=False)
|
||||
grantee_id = db.Column(
|
||||
INTEGER(unsigned=True),
|
||||
db.ForeignKey('users.id', ondelete='SET NULL'),
|
||||
nullable=True,
|
||||
)
|
||||
wait_days = db.Column(db.Integer, default=7, nullable=False)
|
||||
# invited | accepted | ready | pending | denied
|
||||
status = db.Column(db.String(20), default='invited', nullable=False)
|
||||
request_initiated_at = db.Column(db.DateTime, nullable=True)
|
||||
# JSON string: [{ id, name, item_type, enc_data, iv }, ...]
|
||||
enc_vault = db.Column(db.Text, nullable=True)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
|
||||
|
||||
@property
|
||||
def wait_elapsed(self):
|
||||
"""True if the wait period has passed since the access request."""
|
||||
if self.status != 'pending' or not self.request_initiated_at:
|
||||
return False
|
||||
return datetime.utcnow() >= self.request_initiated_at + timedelta(days=self.wait_days)
|
||||
|
||||
def to_dict(self, grantor_email=None):
|
||||
return {
|
||||
'id': self.id,
|
||||
'grantor_id': self.grantor_id,
|
||||
'grantor_email': grantor_email,
|
||||
'grantee_email': self.grantee_email,
|
||||
'grantee_id': self.grantee_id,
|
||||
'wait_days': self.wait_days,
|
||||
'status': self.status,
|
||||
'wait_elapsed': self.wait_elapsed,
|
||||
'request_initiated_at': (
|
||||
self.request_initiated_at.isoformat() if self.request_initiated_at else None
|
||||
),
|
||||
'created_at': self.created_at.isoformat() if self.created_at else None,
|
||||
}
|
||||
Reference in New Issue
Block a user