diff --git a/src/main/java/com/gitrekt/resort/controller/ResetEmployeePasswordDialogController.java b/src/main/java/com/gitrekt/resort/controller/ResetEmployeePasswordDialogController.java index 3c7caed..c43d26b 100644 --- a/src/main/java/com/gitrekt/resort/controller/ResetEmployeePasswordDialogController.java +++ b/src/main/java/com/gitrekt/resort/controller/ResetEmployeePasswordDialogController.java @@ -1,12 +1,20 @@ package com.gitrekt.resort.controller; +import com.gitrekt.resort.model.entities.Employee; +import com.gitrekt.resort.model.services.EmployeeService; +import com.gitrekt.resort.model.services.PasswordValidatorUtil; import java.net.URL; import java.util.ResourceBundle; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Button; +import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.stage.Stage; +import javax.persistence.EntityNotFoundException; +import org.passay.PasswordData; +import org.passay.RuleResult; +import org.passay.RuleResultDetail; /** * Controller class for the dialog in which employees reset their password. @@ -20,43 +28,162 @@ public class ResetEmployeePasswordDialogController implements Initializable { private Button confirmButton; @FXML - private TextField authorizingManagerIdField; + private TextField newPasswordField; @FXML - private TextField authorizingManagerPasswordField; + private TextField confirmPasswordField; @FXML - private TextField confirmEmployeeIdField; + private Label errorLabel; @FXML - private TextField newPasswordField; + private Label lengthLabel; @FXML - private TextField confirmPasswordField; + private Label specialCharLabel; + + @FXML + private Label mixedCaseLabel; + + @FXML + private Label numberLabel; + + private Long employeeId; /** * Initializes the controller class. */ @Override public void initialize(URL url, ResourceBundle rb) { - // TODO + // Configure text change listeners for the two fields + newPasswordField.setOnKeyPressed( + e -> onNewPasswordFieldUpdated() + ); + confirmPasswordField.setOnKeyPressed(e -> + onConfirmPasswordFieldUpdated() + ); } - public void onCancelButtonClicked() { + /** + * Called to initialize the requisite data for the dialog. + * + * @param employeeId The employee id of the employee to reset the password + * for. + */ + public void setEmployeeId(Long employeeId) { + this.employeeId = employeeId; + } + + @FXML + private void onCancelButtonClicked() { Stage dialogStage = (Stage) cancelButton.getScene().getWindow(); dialogStage.close(); } - public void onConfirmButtonClicked() { - // TODO + @FXML + private void onConfirmButtonClicked() { + if(validatePasswords()) { + String password = newPasswordField.getText(); + changePassword(password); + closeDialog(); + } + } + + private void onConfirmPasswordFieldUpdated() { + validatePasswords(); } - public void onConfirmPasswordFieldUpdated() { - // TODO + private void onNewPasswordFieldUpdated() { + validatePasswords(); } - public void onNewPasswordFieldUpdated() { - // TODO + /** + * In addition to determining whether the passwords are valid, this method + * also handles the ui cues that let the user know what is wrong with their + * password. Maybe not the best instance of the SRP, but it's simple enough + * in practice. + * + * @return True if the passwords entered are valid. + */ + private boolean validatePasswords() { + boolean result = true; + + String newPassword = newPasswordField.getText(); + String matchingPassword = confirmPasswordField.getText(); + + // Hide any already showing errors + errorLabel.setVisible(false); + lengthLabel.getStyleClass().remove("validationErrorText"); + specialCharLabel.getStyleClass().remove("validationErrorText"); + numberLabel.getStyleClass().remove("validationErrorText"); + mixedCaseLabel.getStyleClass().remove("validationErrorText"); + + if(newPassword.isEmpty() || matchingPassword.isEmpty()) { + errorLabel.setVisible(true); + errorLabel.setText("Fields cannot be empty"); + result = false; + } + + if(!newPassword.equals(matchingPassword)) { + errorLabel.setVisible(true); + errorLabel.setText("Passwords don't match"); + result = false; + } + + RuleResult ruleResult = PasswordValidatorUtil.validator + .validate(new PasswordData(newPassword)); + + if(!ruleResult.isValid()) { + result = false; + } + + // Update the requirements labels + for(RuleResultDetail d : ruleResult.getDetails()) { + String errorCode = d.getErrorCode(); + switch(errorCode) { + case "TOO_SHORT": + lengthLabel.getStyleClass().add("validationErrorText"); + break; + case "INSUFFICIENT_LOWERCASE": + case "INSUFFICIENT_UPPERCASE": + mixedCaseLabel.getStyleClass().add("validationErrorText"); + break; + case "INSUFFICIENT_DIGIT": + numberLabel.getStyleClass().add("validationErrorText"); + break; + case "INSUFFICIENT_SPECIAL": + specialCharLabel.getStyleClass().add("validationErrorText"); + break; + } + } + + return result; + } + + /** + * Handles the actual act of changing the user's password. + * + * @param newPassword The password, which should be already validated. + */ + private void changePassword(String newPassword) { + // Ensure that we have an employee to change the password for + if(this.employeeId == null) { + throw new IllegalStateException("Must initialize employeeId"); + } + + EmployeeService employeeService = new EmployeeService(); + Employee employee = employeeService.getEmployeeById(this.employeeId); + try { + employee.setPassword(newPassword); + employeeService.updateEmployee(employee); + } catch (EntityNotFoundException e) { + // TODO + } + } + + private void closeDialog() { + Stage dialogStage = (Stage) this.lengthLabel.getScene().getWindow(); + dialogStage.close(); } } diff --git a/src/main/java/com/gitrekt/resort/controller/ScreenManager.java b/src/main/java/com/gitrekt/resort/controller/ScreenManager.java index 0f95b49..c317816 100644 --- a/src/main/java/com/gitrekt/resort/controller/ScreenManager.java +++ b/src/main/java/com/gitrekt/resort/controller/ScreenManager.java @@ -48,8 +48,10 @@ public void initialize(Stage mainStage) { /** * @param fxmlPath The path to the FXML file of the screen to switch to. + * + * @return The FXML controller of the new scene. */ - public void switchToScreen(String fxmlPath) { + public Object switchToScreen(String fxmlPath) { Parent newScreenRoot; try { @@ -61,6 +63,7 @@ public void switchToScreen(String fxmlPath) { mainStage.getScene().setRoot(newScreenRoot); mainStage.setMaximized(true); + return fxmlLoader.getController(); } /** diff --git a/src/main/java/com/gitrekt/resort/controller/StaffAccountsScreenController.java b/src/main/java/com/gitrekt/resort/controller/StaffAccountsScreenController.java index 298f910..b5d184b 100644 --- a/src/main/java/com/gitrekt/resort/controller/StaffAccountsScreenController.java +++ b/src/main/java/com/gitrekt/resort/controller/StaffAccountsScreenController.java @@ -9,6 +9,8 @@ import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.control.Button; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; import javafx.scene.image.Image; import javafx.stage.Modality; import javafx.stage.Stage; @@ -30,6 +32,18 @@ public class StaffAccountsScreenController implements Initializable { @FXML private Button addNewEmployeeButton; + @FXML + private TableView staffAccountTableView; + + @FXML + private TableColumn idColumn; + + @FXML + private TableColumn nameColumn; + + @FXML + private TableColumn managerColumn; + private final Image appLogo = new Image("images/Logo.png"); /** @@ -53,10 +67,12 @@ public void onRemoveEmployeeButtonClicked() { public void onResetEmployeePasswordButtonClicked() throws IOException { Stage dialogStage = new Stage(); dialogStage.getIcons().add(appLogo); - Parent dialogRoot = FXMLLoader.load( + FXMLLoader loader = new FXMLLoader( getClass().getResource("/fxml/ResetEmployeePasswordDialog.fxml") ); + Parent dialogRoot = loader.load(); Scene resetPasswordDialog = new Scene(dialogRoot); + dialogStage.setScene(resetPasswordDialog); dialogStage.initModality(Modality.APPLICATION_MODAL); dialogStage.initOwner( @@ -66,6 +82,11 @@ public void onResetEmployeePasswordButtonClicked() throws IOException { dialogStage.setTitle("Authentication Required"); dialogStage.centerOnScreen(); + ResetEmployeePasswordDialogController c; + c = (ResetEmployeePasswordDialogController) loader.getController(); + long employeeId = getSelectedEmployeeId(); + c.setEmployeeId(employeeId); + dialogStage.show(); } @@ -88,4 +109,9 @@ public void onAddNewEmployeeButtonClicked() throws IOException { dialogStage.show(); } + private long getSelectedEmployeeId() { + // TODO: REPLACE WITH REAL IMPLEMENTATION + return 1L; + } + } diff --git a/src/main/java/com/gitrekt/resort/model/entities/Employee.java b/src/main/java/com/gitrekt/resort/model/entities/Employee.java index eb29d7e..0237404 100644 --- a/src/main/java/com/gitrekt/resort/model/entities/Employee.java +++ b/src/main/java/com/gitrekt/resort/model/entities/Employee.java @@ -62,8 +62,8 @@ public void setManager(boolean isManager) { this.isManager = isManager; } - public void setHashedPassword(String hashedPassword){ - this.hashedPassword = hashedPassword; + public void setPassword(String plaintextPassword){ + encryptPassword(plaintextPassword); } private void encryptPassword(String plaintextPassword) { diff --git a/src/main/java/com/gitrekt/resort/model/services/PasswordValidatorUtil.java b/src/main/java/com/gitrekt/resort/model/services/PasswordValidatorUtil.java new file mode 100644 index 0000000..a7d19c9 --- /dev/null +++ b/src/main/java/com/gitrekt/resort/model/services/PasswordValidatorUtil.java @@ -0,0 +1,19 @@ +package com.gitrekt.resort.model.services; + +import org.passay.CharacterRule; +import org.passay.EnglishCharacterData; +import org.passay.LengthRule; +import org.passay.PasswordValidator; + +public class PasswordValidatorUtil { + + public static final PasswordValidator validator = new PasswordValidator( + // 60 is around the max length of BCrypt passwords anyways + new LengthRule(10,60), + new CharacterRule(EnglishCharacterData.UpperCase, 1), + new CharacterRule(EnglishCharacterData.LowerCase, 1), + new CharacterRule(EnglishCharacterData.Digit, 1), + new CharacterRule(EnglishCharacterData.Special, 1) + ); + +} diff --git a/src/main/resources/fxml/ResetEmployeePasswordDialog.fxml b/src/main/resources/fxml/ResetEmployeePasswordDialog.fxml index 6b58681..86819c2 100644 --- a/src/main/resources/fxml/ResetEmployeePasswordDialog.fxml +++ b/src/main/resources/fxml/ResetEmployeePasswordDialog.fxml @@ -5,14 +5,13 @@ - - + @@ -23,33 +22,12 @@ - - - - - - - - - - - - - - - - - - - - -