Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Implementing Secure Authentication with PyQt5 and Database Integration

Tech 1

Environment Setup

To develop the login interface, ensure the following development stack is configured:

  • Operating System: Windows 10 or later (compatible with Linux/macOS)
  • IDE/Editor: Sublime Text 3 or VS Code
  • Language: Python 3.x with PyQt5 bindings

Functional Requirements

The authentication module must handle the following logic flow:

  1. Input Validation: Restrict character length and type for username and password fields.
  2. Password Masking: Ensure passwords are obscured in the UI field.
  3. Verification: Compare entered credentials against stored database records.
  4. Error Handling: Provide feedback for empty inputs, non-existent accounts, or incorrect passwords.
  5. Role Dispatch: Emit specific signals upon successful authentication to determine if the user is an administrator or a standard student.

User Interface Design

The layout utilizes QFormLayout for aligning labels and input fields, wrapped within horizontal and vertical layouts for centering.

Components:

  • One header label for system branding.
  • Two input fields for ID and Password entry.
  • Two action buttons: "Login" and "Clear".
  • Custom font sizing and fixed widget dimensions for consistency.

Implementation Details

Layout Configuration

The visual structure relies on nested widgets to manage spacing and alignment effectively.

def build_layout(self):
    self.main_vertical = QVBoxLayout(self)
    
    # Header Section
    self.header_label = QLabel("Library Management System")
    font_title = QFont()
    font_title.setPixelSize(28)
    self.header_label.setFont(font_title)
    self.header_label.setAlignment(Qt.AlignCenter)
    
    # Input Form
    self.form_layout = QFormLayout()
    self.form_layout.setSpacing(10)
    
    # Username Field
    self.username_lbl = QLabel("User ID : ")
    self.user_input = QLineEdit()
    self.user_input.setFixedWidth(200)
    self.user_input.setMaxLength(10)
    self.user_input.setFont(QFont(None, 16))
    
    # Password Field
    self.pass_lbl = QLabel("Password : ")
    self.pwd_input = QLineEdit()
    self.pwd_input.setFixedWidth(200)
    self.pwd_input.setMaxLength(16)
    self.pwd_input.setEchoMode(QLineEdit.PasswordEchoOnEdit)
    self.pwd_input.setFont(QFont(None, 16))
    
    # Validators
    id_validator = QRegExpValidator(QRegExp(r"[A-Za-z0-9]{8,}"), self)
    pwd_validator = QRegExpValidator(QRegExp(r"^[a-zA-Z0-9]+$"), self)
    self.user_input.setValidator(id_validator)
    self.pwd_input.setValidator(pwd_validator)
    
    self.form_layout.addRow(self.username_lbl, self.user_input)
    self.form_layout.addRow(self.pass_lbl, self.pwd_input)
    
    # Buttons Area
    self.btn_container = QWidget()
    btn_layout = QHBoxLayout(self.btn_container)
    self.btn_login = QPushButton("Log In")
    self.btn_reset = QPushButton("Reset")
    self.btn_login.setMinimumHeight(30)
    self.btn_reset.setMinimumHeight(30)
    
    btn_layout.addWidget(self.btn_login)
    btn_layout.addWidget(self.btn_reset)
    self.form_layout.addRow(self.btn_container)
    
    # Main Widget Assembly
    form_widget = QWidget()
    form_widget.setLayout(self.form_layout)
    
    self.container = QWidget()
    content_hbox = QHBoxLayout(self.container)
    content_hbox.addStretch()
    content_hbox.addWidget(form_widget)
    content_hbox.addStretch()
    
    self.main_vertical.addWidget(self.header_label)
    self.main_vertical.addWidget(self.container)

Initialization Logic

Upon instantiation, the widget initializes the database manager reference and sets window properties.

def __init__(self):
    super(AuthScreen, self).__init__()
    self.resize(900, 600)
    self.setWindowTitle("Authentication Portal")
    self.build_layout()
    self.db_manager = UserDbManager()

Signal Connections

Event listeners bind UI interactions to handler methods.

# Connect button clicks and enter key presses
self.btn_login.clicked.connect(self.handle_login)
self.btn_reset.clicked.connect(self.handle_clear)
self.user_input.returnPressed.connect(self.handle_login)
self.pwd_input.returnPressed.connect(self.handle_login)

Business Logic

The core verification process hashes the input password using MD5 before qeurying the database.

1. Clear Functionality Resets input fields to default state.

def handle_clear(self):
    self.user_input.clear()
    self.pwd_input.clear()
    self.user_input.setFocus()

2. Login Verification Handles security checks and role detection.

def handle_login(self):
    user_id = self.user_input.text()
    secret = self.pwd_input.text()

    # Validate non-empty inputs
    if not user_id or not secret:
        QMessageBox.warning(self, "Warning", "Credentials cannot be empty!")
        return

    # Hash the input password
    hasher = hashlib.md5()
    hasher.update(secret.encode('utf-8'))
    hashed_secret = hasher.hexdigest()

    # Query database
    record = self.db_manager.query_user(user_id)

    if not record:
        QMessageBox.information(self, "Info", "Account does not exist.")
        self.handle_clear()
        return

    # Verify hash match
    # Assuming record format: [id, name, hashed_pwd, is_admin_flag, ...]
    db_id, _, db_hash, admin_flag, _ = record[0]

    if db_id == user_id and db_hash == hashed_secret:
        if admin_flag == 1:
            self.admin_access_signal.emit()
        else:
            self.student_access_signal.emit(user_id)
    else:
        QMessageBox.information(self, "Info", "Incorrect password.")

Complete Module Code

This example aggregates the setup into a functional script.

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QLineEdit, QPushButton, \
                             QVBoxLayout, QHBoxLayout, QFormLayout, QMessageBox, QRegExpValidator, QRegExp
from PyQt5.QtGui import QFont, QIcon
from PyQt5.QtCore import Qt, pyqtSignal
import hashlib

# Mock external dependency
# from db.userInfoManager import UserDbManager

class AuthScreen(QWidget):
    admin_access_signal = pyqtSignal()
    student_access_signal = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self.resize(900, 600)
        self.setWindowTitle("System Entry")
        self.setup_ui()
        # Initialize DB connection here
        # self.db_manager = UserDbManager()

    def setup_ui(self):
        self.layout = QVBoxLayout(self)
        
        # Title
        title = QLabel("Library Management Application")
        title_font = QFont()
        title_font.setPointSize(20)
        title.setFont(title_font)
        title.setAlignment(Qt.AlignCenter)
        title.setStyleSheet("font-weight: bold;")
        
        # Form Container
        container = QWidget()
        h_box = QHBoxLayout(container)
        
        # Fields
        form = QFormLayout()
        self.txt_username = QLineEdit()
        self.txt_password = QLineEdit()
        self.txt_password.setEchoMode(QLineEdit.PasswordEchoOnEdit)
        
        # Validation Rules
        reg_id = QRegExp(r"[A-Za-z0-9]{8,}")
        reg_pwd = QRegExp(r"[a-zA-Z0-9]+")+"")
        validator = QRegExpValidator(reg_id)
        self.txt_username.setValidator(QRegExpValidator(reg_id))
        self.txt_password.setValidator(QRegExpValidator(reg_pwd))
        
        form.addRow("Username:", self.txt_username)
        form.addRow("Password:", self.txt_password)
        
        # Buttons
        btn_frame = QWidget()
        btn_layout = QHBoxLayout(btn_frame)
        self.btn_signin = QPushButton("Sign In")
        self.btn_cancel = QPushButton("Reset")
        btn_layout.addWidget(self.btn_signin)
        btn_layout.addWidget(self.btn_cancel)
        form.addRow(btn_frame)
        
        h_box.addStretch()
        h_box.addLayout(form)
        h_box.addStretch()
        
        self.layout.addWidget(title)
        self.layout.addWidget(container)
        
        # Bind Events
        self.btn_signin.clicked.connect(self.check_credentials)
        self.btn_cancel.clicked.connect(self.reset_fields)
        self.txt_username.returnPressed.connect(self.check_credentials)
        self.txt_password.returnPressed.connect(self.check_credentials)

    def reset_fields(self):
        self.txt_username.clear()
        self.txt_password.clear()
        self.txt_username.setFocus()

    def check_credentials(self):
        uid = self.txt_username.text().strip()
        pswd = self.txt_password.text()
        
        if not uid or not pswd:
            QMessageBox.warning(self, "Error", "Please fill in all fields.")
            return
            
        # Simulated DB lookup instead of actual call for standalone integrity
        # record = self.db_manager.query_user(uid)
        
        # --- Logic Simulation Start ---
        # In production, uncomment below block
        # if not record:
        #     QMessageBox.warning(self, "Notice", "User not found.")
        #     self.reset_fields()
        # else:
        #     pass_hash = hashlib.md5(pswd.encode('utf-8')).hexdigest()
        #     db_pass = record[0][2] # Adjust index based on schema
        #     if pass_hash == db_pass:
        #         if record[0][3] == 1:
        #             self.admin_access_signal.emit()
        #         else:
        #             self.student_access_signal.emit(uid)
        #     else:
        #         QMessageBox.warning(self, "Alert", "Wrong password.")
        # --- Logic Simulation End ---
        
        QMessageBox.information(self, "Demo", "Simulation completed.")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = AuthScreen()
    window.show()
    sys.exit(app.exec_())
Tags: Pythonpyqt5

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.