diff --git a/src/main/java/org/example/petshopdesktop/auth/Role.java b/src/main/java/org/example/petshopdesktop/auth/Role.java index 64f38459..e631ddf7 100644 --- a/src/main/java/org/example/petshopdesktop/auth/Role.java +++ b/src/main/java/org/example/petshopdesktop/auth/Role.java @@ -1,6 +1,13 @@ package org.example.petshopdesktop.auth; +/* +Petshop Desktop +Purpose: Application role definitions used by session state and role based access control. +*/ public enum Role { + // Administrative access, includes system management screens. ADMIN, + + // Staff access, limited to day to day operational screens. STAFF } diff --git a/src/main/java/org/example/petshopdesktop/auth/UserSession.java b/src/main/java/org/example/petshopdesktop/auth/UserSession.java index 748c9546..2cc4538a 100644 --- a/src/main/java/org/example/petshopdesktop/auth/UserSession.java +++ b/src/main/java/org/example/petshopdesktop/auth/UserSession.java @@ -1,14 +1,24 @@ package org.example.petshopdesktop.auth; +/* +Petshop Desktop +Purpose: In memory session state for the authenticated user. +Notes: Session is process local and cleared on logout or application restart. +*/ public class UserSession { + // Singleton instance used to share session state across controllers. private static UserSession instance; + // Current authenticated username, null when logged out. private String username; + + // Current authenticated role, null when logged out. private Role role; private UserSession() {} + // Lazily initialised singleton accessor. public static UserSession getInstance() { if (instance == null) { instance = new UserSession(); @@ -16,11 +26,13 @@ public class UserSession { return instance; } + // Stores identity and role for the active session. public void login(String username, Role role) { this.username = username; this.role = role; } + // Clears session state and returns the application to an unauthenticated state. public void logout() { this.username = null; this.role = null; @@ -34,10 +46,13 @@ public class UserSession { return role; } + // Convenience check for administrative privileges. + // Role.ADMIN.equals(role) remains safe when role is null. public boolean isAdmin() { return Role.ADMIN.equals(role); } + // Session is considered active only when both username and role are set. public boolean isLoggedIn() { return username != null && role != null; } diff --git a/src/main/java/org/example/petshopdesktop/controllers/LoginController.java b/src/main/java/org/example/petshopdesktop/controllers/LoginController.java index 43bb3334..440c5686 100644 --- a/src/main/java/org/example/petshopdesktop/controllers/LoginController.java +++ b/src/main/java/org/example/petshopdesktop/controllers/LoginController.java @@ -14,6 +14,10 @@ import org.example.petshopdesktop.models.User; import java.sql.SQLException; +/* +Petshop Desktop +Purpose: Authentication controller responsible for validating credentials and initialising the user session. +*/ public class LoginController { @FXML @@ -27,15 +31,18 @@ public class LoginController { @FXML void btnLoginClicked(ActionEvent event) { + // Input normalisation keeps authentication behaviour consistent. String username = txtUsername.getText().trim(); String password = txtPassword.getText(); + // Basic validation to avoid unnecessary database calls. if (username.isEmpty() || password.isEmpty()) { lblError.setText("Please enter username and password."); return; } try { + // Credential verification returns a fully populated User on success. User user = UserDB.authenticate(username, password); if (user == null) { lblError.setText("Invalid username or password."); @@ -43,6 +50,7 @@ public class LoginController { return; } + // Session state is stored in memory for use by controllers and UI RBAC. UserSession.getInstance().login(user.getUsername(), user.getRole()); openMainLayout(); @@ -53,6 +61,7 @@ public class LoginController { private void openMainLayout() { try { + // View transition into the post login application shell. FXMLLoader loader = new FXMLLoader( getClass().getResource("/org/example/petshopdesktop/main-layout-view.fxml")); Scene scene = new Scene(loader.load()); diff --git a/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java b/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java index 3584a0cc..a910432e 100644 --- a/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java +++ b/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java @@ -11,6 +11,10 @@ import javafx.scene.layout.StackPane; import javafx.stage.Stage; import org.example.petshopdesktop.auth.UserSession; +/* +Petshop Desktop +Purpose: Main application shell controller, includes navigation and UI level role based access control. +*/ public class MainLayoutController { @FXML @@ -108,6 +112,7 @@ public class MainLayoutController { @FXML void btnLogoutClicked(ActionEvent event) { + // Logout clears session state before returning to the login view. UserSession.getInstance().logout(); try { FXMLLoader loader = new FXMLLoader( @@ -124,16 +129,22 @@ public class MainLayoutController { @FXML public void initialize() { + // RBAC state is applied once during initial layout load. applyRBAC(); + + // Default landing view after successful authentication. loadView("pet-view.fxml"); } private void applyRBAC() { UserSession session = UserSession.getInstance(); + // Session identity is displayed in the header for clarity and auditing. lblUsername.setText(session.getUsername()); lblRole.setText(session.getRole().toString()); + // UI level RBAC hides admin only navigation entries for non admin users. + // setManaged(false) removes the node from layout calculations to avoid empty spacing. boolean isAdmin = session.isAdmin(); btnInventory.setVisible(isAdmin); btnInventory.setManaged(isAdmin); @@ -141,6 +152,8 @@ public class MainLayoutController { btnSuppliers.setManaged(isAdmin); btnProductSuppliers.setVisible(isAdmin); btnProductSuppliers.setManaged(isAdmin); + + // Privileged operations should still be enforced within the relevant controllers and database methods. } /** diff --git a/src/main/java/org/example/petshopdesktop/database/UserDB.java b/src/main/java/org/example/petshopdesktop/database/UserDB.java index 8909d211..3b5f78b4 100644 --- a/src/main/java/org/example/petshopdesktop/database/UserDB.java +++ b/src/main/java/org/example/petshopdesktop/database/UserDB.java @@ -5,6 +5,10 @@ import org.example.petshopdesktop.models.User; import java.sql.*; +/* +Petshop Desktop +Purpose: User authentication and role lookup against the users table. +*/ public class UserDB { /** @@ -29,7 +33,11 @@ public class UserDB { if (rs.next()) { int userId = rs.getInt("user_id"); String uname = rs.getString("username"); + + // Role values are stored in the database as strings and normalised to match the enum. + // Table constraints limit role values, Role.valueOf is expected to be safe under normal operation. Role role = Role.valueOf(rs.getString("role").toUpperCase()); + return new User(userId, uname, role); } } @@ -51,6 +59,7 @@ public class UserDB { ) """; + // Default accounts support initial development and testing, credentials should be rotated or removed for deployment. String seedAdmin = """ INSERT IGNORE INTO users (username, password_hash, role) VALUES ('admin', SHA2('admin123', 256), 'ADMIN')