from datetime import datetime from app.extensions import db class User(db.Model): """ Tracked employees / users of IT assets. Privacy / GDPR masking: When a user leaves the company, an admin can mask the record. All PII fields are cleared and replaced with a reference to the permanent windows_id so asset history is preserved without exposing personal data during audits. """ __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) # Permanent, non-PII identifier — used as the anchor for history after masking windows_id = db.Column(db.String(50), unique=True, nullable=False, index=True) # PII fields — nulled out when masked first_name = db.Column(db.String(100), nullable=True) last_name = db.Column(db.String(100), nullable=True) email = db.Column(db.String(200), nullable=True) phone = db.Column(db.String(50), nullable=True) # Non-PII organisational data — retained after masking department = db.Column(db.String(100), nullable=True) job_title = db.Column(db.String(100), nullable=True) location = db.Column(db.String(100), nullable=True) manager_windows_id = db.Column(db.String(50), nullable=True) # Status is_active = db.Column(db.Boolean, default=True) # employed / active in company is_masked = db.Column(db.Boolean, default=False) # PII erased masked_at = db.Column(db.DateTime, nullable=True) masked_by_id = db.Column(db.Integer, db.ForeignKey('admin_users.id'), nullable=True) # Import metadata import_source = db.Column(db.String(20), default='manual') # manual | ldap | csv ldap_dn = db.Column(db.String(500), nullable=True) # AD Distinguished Name created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) # Relationships assignments = db.relationship( 'Assignment', foreign_keys='Assignment.user_id', backref='user', lazy='dynamic', cascade='all, delete-orphan' ) paperwork_docs = db.relationship( 'Paperwork', foreign_keys='Paperwork.user_id', backref='user', lazy='dynamic', cascade='all, delete-orphan' ) masked_by = db.relationship('AdminUser', foreign_keys=[masked_by_id]) # ------------------------------------------------------------------ # Display helpers # ------------------------------------------------------------------ @property def display_name(self): if self.is_masked: return f'[MASKED – WID: {self.windows_id}]' parts = [self.first_name, self.last_name] full = ' '.join(p for p in parts if p) return full or self.windows_id @property def display_email(self): return '[MASKED]' if self.is_masked else (self.email or '—') @property def display_phone(self): return '[MASKED]' if self.is_masked else (self.phone or '—') @property def current_assets(self): """Returns active assignments.""" return self.assignments.filter_by(is_active=True).all() def mask(self, admin_user_id): """Erase PII while preserving the record for asset-history purposes.""" self.first_name = None self.last_name = None self.email = None self.phone = None self.is_active = False self.is_masked = True self.masked_at = datetime.utcnow() self.masked_by_id = admin_user_id def __repr__(self): return f''