05/02/2026 updated code for security 2
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy.dialects.mysql import INTEGER
|
||||
|
||||
@@ -21,7 +21,7 @@ class AuditLog(db.Model):
|
||||
resource_id = db.Column(INTEGER(unsigned=True), nullable=True) # FK to the affected row
|
||||
detail = db.Column(db.String(512), nullable=True) # human-readable summary (no secrets)
|
||||
ip_address = db.Column(db.String(45), nullable=True) # IPv4 or IPv6
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False, index=True)
|
||||
created_at = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc).replace(tzinfo=None), nullable=False, index=True)
|
||||
|
||||
@classmethod
|
||||
def log(cls, user_id: int, action: str, resource_type: str,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timezone, timedelta
|
||||
|
||||
from sqlalchemy.dialects.mysql import INTEGER
|
||||
|
||||
@@ -39,14 +39,14 @@ class EmergencyAccess(db.Model):
|
||||
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)
|
||||
created_at = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc).replace(tzinfo=None), 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)
|
||||
return datetime.now(timezone.utc).replace(tzinfo=None) >= self.request_initiated_at + timedelta(days=self.wait_days)
|
||||
|
||||
def to_dict(self, grantor_email=None):
|
||||
return {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy.dialects.mysql import INTEGER
|
||||
|
||||
@@ -40,7 +40,7 @@ class SharedItem(db.Model):
|
||||
iv = db.Column(db.String(64), nullable=False)
|
||||
|
||||
accepted = db.Column(db.Boolean, default=False, nullable=False)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
|
||||
created_at = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc).replace(tzinfo=None), nullable=False)
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy.dialects.mysql import INTEGER
|
||||
|
||||
@@ -20,10 +20,10 @@ class TokenBlacklist(db.Model):
|
||||
if not entry:
|
||||
return False
|
||||
# Automatically ignore expired entries (they can be cleaned up later)
|
||||
return entry.expires_at > datetime.utcnow()
|
||||
return entry.expires_at > datetime.now(timezone.utc).replace(tzinfo=None)
|
||||
|
||||
@classmethod
|
||||
def cleanup_expired(cls):
|
||||
"""Delete entries that have already expired — call occasionally to keep table small."""
|
||||
cls.query.filter(cls.expires_at <= datetime.utcnow()).delete()
|
||||
cls.query.filter(cls.expires_at <= datetime.now(timezone.utc).replace(tzinfo=None)).delete()
|
||||
db.session.commit()
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
from flask_login import UserMixin
|
||||
from argon2 import PasswordHasher
|
||||
from argon2.exceptions import VerifyMismatchError, VerificationError, InvalidHashError
|
||||
@@ -19,7 +19,7 @@ class User(db.Model, UserMixin):
|
||||
# Returned to the client on login so it can re-derive the AES-256-GCM vault key.
|
||||
# The server never uses this for decryption — it is opaque to us.
|
||||
enc_key_salt = db.Column(db.String(64), nullable=False)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
|
||||
created_at = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc).replace(tzinfo=None), nullable=False)
|
||||
last_login = db.Column(db.DateTime, nullable=True)
|
||||
# TOTP / MFA
|
||||
# totp_secret: AES-256-GCM ciphertext of the base32 TOTP secret, base64-encoded.
|
||||
@@ -60,4 +60,4 @@ class User(db.Model, UserMixin):
|
||||
return False
|
||||
|
||||
def __repr__(self):
|
||||
return f'<User {self.email}>'
|
||||
return f'<User {self.email}>'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
import enum
|
||||
|
||||
from sqlalchemy.dialects.mysql import INTEGER
|
||||
@@ -34,8 +34,8 @@ class VaultItem(db.Model):
|
||||
# the plaintext 'name' column when enc_name is absent.
|
||||
enc_name = db.Column(db.Text, nullable=True)
|
||||
iv_name = db.Column(db.String(64), nullable=True)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
|
||||
created_at = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc).replace(tzinfo=None), nullable=False)
|
||||
updated_at = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc).replace(tzinfo=None), onupdate=lambda: datetime.now(timezone.utc).replace(tzinfo=None), nullable=False)
|
||||
|
||||
def to_dict(self):
|
||||
# item_type is stored as a plain string; handle both str and enum safely
|
||||
|
||||
Reference in New Issue
Block a user