"""User and authentication models.""" from datetime import datetime from shopdb.extensions import db from .base import BaseModel # Association table for user roles (many-to-many) userroles = db.Table( 'userroles', db.Column('userid', db.Integer, db.ForeignKey('users.userid'), primary_key=True), db.Column('roleid', db.Integer, db.ForeignKey('roles.roleid'), primary_key=True) ) # Association table for role permissions (many-to-many) rolepermissions = db.Table( 'rolepermissions', db.Column('roleid', db.Integer, db.ForeignKey('roles.roleid'), primary_key=True), db.Column('permissionid', db.Integer, db.ForeignKey('permissions.permissionid'), primary_key=True) ) class Permission(db.Model): """ Permission model for granular access control. Permissions are predefined and assigned to roles. """ __tablename__ = 'permissions' permissionid = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(50), unique=True, nullable=False) description = db.Column(db.String(255)) category = db.Column(db.String(50), default='general') # For grouping in UI # Predefined permissions PERMISSIONS = [ # Assets ('assets.view', 'View assets', 'assets'), ('assets.create', 'Create assets', 'assets'), ('assets.edit', 'Edit assets', 'assets'), ('assets.delete', 'Delete assets', 'assets'), # Equipment ('equipment.view', 'View equipment', 'equipment'), ('equipment.create', 'Create equipment', 'equipment'), ('equipment.edit', 'Edit equipment', 'equipment'), ('equipment.delete', 'Delete equipment', 'equipment'), # Computers ('computers.view', 'View computers', 'computers'), ('computers.create', 'Create computers', 'computers'), ('computers.edit', 'Edit computers', 'computers'), ('computers.delete', 'Delete computers', 'computers'), # Printers ('printers.view', 'View printers', 'printers'), ('printers.create', 'Create printers', 'printers'), ('printers.edit', 'Edit printers', 'printers'), ('printers.delete', 'Delete printers', 'printers'), # Network ('network.view', 'View network devices', 'network'), ('network.create', 'Create network devices', 'network'), ('network.edit', 'Edit network devices', 'network'), ('network.delete', 'Delete network devices', 'network'), # Applications ('applications.view', 'View applications', 'applications'), ('applications.create', 'Create applications', 'applications'), ('applications.edit', 'Edit applications', 'applications'), ('applications.delete', 'Delete applications', 'applications'), # Knowledge Base ('kb.view', 'View knowledge base', 'knowledgebase'), ('kb.create', 'Create KB articles', 'knowledgebase'), ('kb.edit', 'Edit KB articles', 'knowledgebase'), ('kb.delete', 'Delete KB articles', 'knowledgebase'), # Notifications ('notifications.view', 'View notifications', 'notifications'), ('notifications.create', 'Create notifications', 'notifications'), ('notifications.edit', 'Edit notifications', 'notifications'), ('notifications.delete', 'Delete notifications', 'notifications'), # Reports ('reports.view', 'View reports', 'reports'), ('reports.export', 'Export reports', 'reports'), # Settings ('settings.view', 'View settings', 'admin'), ('settings.edit', 'Edit settings', 'admin'), # Users ('users.view', 'View users', 'admin'), ('users.create', 'Create users', 'admin'), ('users.edit', 'Edit users', 'admin'), ('users.delete', 'Delete users', 'admin'), # Audit ('audit.view', 'View audit logs', 'admin'), ] def __repr__(self): return f"" @classmethod def seed(cls): """Seed predefined permissions.""" created = 0 for name, description, category in cls.PERMISSIONS: if not cls.query.filter_by(name=name).first(): perm = cls(name=name, description=description, category=category) db.session.add(perm) created += 1 return created class Role(BaseModel): """User role model.""" __tablename__ = 'roles' roleid = db.Column(db.Integer, primary_key=True) rolename = db.Column(db.String(50), unique=True, nullable=False) description = db.Column(db.Text) # Permissions relationship permissions = db.relationship( 'Permission', secondary=rolepermissions, backref=db.backref('roles', lazy='dynamic') ) def __repr__(self): return f"" def haspermission(self, permission_name: str) -> bool: """Check if role has a specific permission.""" # Admin role has all permissions if self.rolename == 'admin': return True return any(p.name == permission_name for p in self.permissions) def getpermissionnames(self) -> list: """Get list of permission names.""" if self.rolename == 'admin': return [p[0] for p in Permission.PERMISSIONS] return [p.name for p in self.permissions] class User(BaseModel): """User model for authentication.""" __tablename__ = 'users' userid = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(100), unique=True, nullable=False, index=True) email = db.Column(db.String(255), unique=True, nullable=False) passwordhash = db.Column(db.String(255), nullable=False) # Profile firstname = db.Column(db.String(100)) lastname = db.Column(db.String(100)) # Status lastlogindate = db.Column(db.DateTime) failedlogins = db.Column(db.Integer, default=0) lockeduntil = db.Column(db.DateTime) # Relationships roles = db.relationship( 'Role', secondary=userroles, backref=db.backref('users', lazy='dynamic') ) def __repr__(self): return f"" @property def islocked(self): """Check if account is locked.""" if self.lockeduntil: return datetime.utcnow() < self.lockeduntil return False def hasrole(self, rolename: str) -> bool: """Check if user has a specific role.""" return any(r.rolename == rolename for r in self.roles) def haspermission(self, permission_name: str) -> bool: """Check if user has a specific permission through any role.""" # Admin role has all permissions if self.hasrole('admin'): return True return any(r.haspermission(permission_name) for r in self.roles) def getpermissions(self) -> list: """Get list of all permission names from all roles.""" if self.hasrole('admin'): return [p[0] for p in Permission.PERMISSIONS] perms = set() for role in self.roles: perms.update(role.getpermissionnames()) return list(perms)