comments
This commit is contained in:
@@ -1,6 +1,13 @@
|
|||||||
package org.example.petshopdesktop.auth;
|
package org.example.petshopdesktop.auth;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Petshop Desktop
|
||||||
|
Purpose: Application role definitions used by session state and role based access control.
|
||||||
|
*/
|
||||||
public enum Role {
|
public enum Role {
|
||||||
|
// Administrative access, includes system management screens.
|
||||||
ADMIN,
|
ADMIN,
|
||||||
|
|
||||||
|
// Staff access, limited to day to day operational screens.
|
||||||
STAFF
|
STAFF
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,24 @@
|
|||||||
package org.example.petshopdesktop.auth;
|
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 {
|
public class UserSession {
|
||||||
|
|
||||||
|
// Singleton instance used to share session state across controllers.
|
||||||
private static UserSession instance;
|
private static UserSession instance;
|
||||||
|
|
||||||
|
// Current authenticated username, null when logged out.
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
|
// Current authenticated role, null when logged out.
|
||||||
private Role role;
|
private Role role;
|
||||||
|
|
||||||
private UserSession() {}
|
private UserSession() {}
|
||||||
|
|
||||||
|
// Lazily initialised singleton accessor.
|
||||||
public static UserSession getInstance() {
|
public static UserSession getInstance() {
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
instance = new UserSession();
|
instance = new UserSession();
|
||||||
@@ -16,11 +26,13 @@ public class UserSession {
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stores identity and role for the active session.
|
||||||
public void login(String username, Role role) {
|
public void login(String username, Role role) {
|
||||||
this.username = username;
|
this.username = username;
|
||||||
this.role = role;
|
this.role = role;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clears session state and returns the application to an unauthenticated state.
|
||||||
public void logout() {
|
public void logout() {
|
||||||
this.username = null;
|
this.username = null;
|
||||||
this.role = null;
|
this.role = null;
|
||||||
@@ -34,10 +46,13 @@ public class UserSession {
|
|||||||
return role;
|
return role;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convenience check for administrative privileges.
|
||||||
|
// Role.ADMIN.equals(role) remains safe when role is null.
|
||||||
public boolean isAdmin() {
|
public boolean isAdmin() {
|
||||||
return Role.ADMIN.equals(role);
|
return Role.ADMIN.equals(role);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Session is considered active only when both username and role are set.
|
||||||
public boolean isLoggedIn() {
|
public boolean isLoggedIn() {
|
||||||
return username != null && role != null;
|
return username != null && role != null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ import org.example.petshopdesktop.models.User;
|
|||||||
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Petshop Desktop
|
||||||
|
Purpose: Authentication controller responsible for validating credentials and initialising the user session.
|
||||||
|
*/
|
||||||
public class LoginController {
|
public class LoginController {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -27,15 +31,18 @@ public class LoginController {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void btnLoginClicked(ActionEvent event) {
|
void btnLoginClicked(ActionEvent event) {
|
||||||
|
// Input normalisation keeps authentication behaviour consistent.
|
||||||
String username = txtUsername.getText().trim();
|
String username = txtUsername.getText().trim();
|
||||||
String password = txtPassword.getText();
|
String password = txtPassword.getText();
|
||||||
|
|
||||||
|
// Basic validation to avoid unnecessary database calls.
|
||||||
if (username.isEmpty() || password.isEmpty()) {
|
if (username.isEmpty() || password.isEmpty()) {
|
||||||
lblError.setText("Please enter username and password.");
|
lblError.setText("Please enter username and password.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Credential verification returns a fully populated User on success.
|
||||||
User user = UserDB.authenticate(username, password);
|
User user = UserDB.authenticate(username, password);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
lblError.setText("Invalid username or password.");
|
lblError.setText("Invalid username or password.");
|
||||||
@@ -43,6 +50,7 @@ public class LoginController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Session state is stored in memory for use by controllers and UI RBAC.
|
||||||
UserSession.getInstance().login(user.getUsername(), user.getRole());
|
UserSession.getInstance().login(user.getUsername(), user.getRole());
|
||||||
openMainLayout();
|
openMainLayout();
|
||||||
|
|
||||||
@@ -53,6 +61,7 @@ public class LoginController {
|
|||||||
|
|
||||||
private void openMainLayout() {
|
private void openMainLayout() {
|
||||||
try {
|
try {
|
||||||
|
// View transition into the post login application shell.
|
||||||
FXMLLoader loader = new FXMLLoader(
|
FXMLLoader loader = new FXMLLoader(
|
||||||
getClass().getResource("/org/example/petshopdesktop/main-layout-view.fxml"));
|
getClass().getResource("/org/example/petshopdesktop/main-layout-view.fxml"));
|
||||||
Scene scene = new Scene(loader.load());
|
Scene scene = new Scene(loader.load());
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ import javafx.scene.layout.StackPane;
|
|||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import org.example.petshopdesktop.auth.UserSession;
|
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 {
|
public class MainLayoutController {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -108,6 +112,7 @@ public class MainLayoutController {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void btnLogoutClicked(ActionEvent event) {
|
void btnLogoutClicked(ActionEvent event) {
|
||||||
|
// Logout clears session state before returning to the login view.
|
||||||
UserSession.getInstance().logout();
|
UserSession.getInstance().logout();
|
||||||
try {
|
try {
|
||||||
FXMLLoader loader = new FXMLLoader(
|
FXMLLoader loader = new FXMLLoader(
|
||||||
@@ -124,16 +129,22 @@ public class MainLayoutController {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
|
// RBAC state is applied once during initial layout load.
|
||||||
applyRBAC();
|
applyRBAC();
|
||||||
|
|
||||||
|
// Default landing view after successful authentication.
|
||||||
loadView("pet-view.fxml");
|
loadView("pet-view.fxml");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyRBAC() {
|
private void applyRBAC() {
|
||||||
UserSession session = UserSession.getInstance();
|
UserSession session = UserSession.getInstance();
|
||||||
|
|
||||||
|
// Session identity is displayed in the header for clarity and auditing.
|
||||||
lblUsername.setText(session.getUsername());
|
lblUsername.setText(session.getUsername());
|
||||||
lblRole.setText(session.getRole().toString());
|
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();
|
boolean isAdmin = session.isAdmin();
|
||||||
btnInventory.setVisible(isAdmin);
|
btnInventory.setVisible(isAdmin);
|
||||||
btnInventory.setManaged(isAdmin);
|
btnInventory.setManaged(isAdmin);
|
||||||
@@ -141,6 +152,8 @@ public class MainLayoutController {
|
|||||||
btnSuppliers.setManaged(isAdmin);
|
btnSuppliers.setManaged(isAdmin);
|
||||||
btnProductSuppliers.setVisible(isAdmin);
|
btnProductSuppliers.setVisible(isAdmin);
|
||||||
btnProductSuppliers.setManaged(isAdmin);
|
btnProductSuppliers.setManaged(isAdmin);
|
||||||
|
|
||||||
|
// Privileged operations should still be enforced within the relevant controllers and database methods.
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ import org.example.petshopdesktop.models.User;
|
|||||||
|
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Petshop Desktop
|
||||||
|
Purpose: User authentication and role lookup against the users table.
|
||||||
|
*/
|
||||||
public class UserDB {
|
public class UserDB {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -29,7 +33,11 @@ public class UserDB {
|
|||||||
if (rs.next()) {
|
if (rs.next()) {
|
||||||
int userId = rs.getInt("user_id");
|
int userId = rs.getInt("user_id");
|
||||||
String uname = rs.getString("username");
|
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());
|
Role role = Role.valueOf(rs.getString("role").toUpperCase());
|
||||||
|
|
||||||
return new User(userId, uname, role);
|
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 = """
|
String seedAdmin = """
|
||||||
INSERT IGNORE INTO users (username, password_hash, role)
|
INSERT IGNORE INTO users (username, password_hash, role)
|
||||||
VALUES ('admin', SHA2('admin123', 256), 'ADMIN')
|
VALUES ('admin', SHA2('admin123', 256), 'ADMIN')
|
||||||
|
|||||||
Reference in New Issue
Block a user