|
1 | 1 | package edu.rpi.aris.assign.client;
|
2 | 2 |
|
| 3 | +import edu.rpi.aris.assign.DBUtils; |
3 | 4 | import edu.rpi.aris.assign.LibAssign;
|
4 | 5 | import edu.rpi.aris.assign.MessageCommunication;
|
5 | 6 | import edu.rpi.aris.assign.NetUtil;
|
6 |
| -import edu.rpi.aris.assign.client.exceptions.AuthBanException; |
7 |
| -import edu.rpi.aris.assign.client.exceptions.InvalidAccessTokenException; |
8 |
| -import edu.rpi.aris.assign.client.exceptions.InvalidCredentialsException; |
| 7 | +import edu.rpi.aris.assign.client.exceptions.*; |
9 | 8 | import edu.rpi.aris.assign.client.model.Config;
|
10 | 9 | import edu.rpi.aris.assign.message.ErrorMsg;
|
11 | 10 | import edu.rpi.aris.assign.message.Message;
|
| 11 | +import edu.rpi.aris.assign.message.UserEditMsg; |
12 | 12 | import javafx.application.Platform;
|
13 | 13 | import javafx.beans.binding.Bindings;
|
14 | 14 | import javafx.beans.property.SimpleObjectProperty;
|
15 | 15 | import javafx.geometry.Insets;
|
16 | 16 | import javafx.geometry.Pos;
|
17 | 17 | import javafx.scene.Node;
|
| 18 | +import javafx.scene.Parent; |
18 | 19 | import javafx.scene.control.*;
|
19 | 20 | import javafx.scene.layout.GridPane;
|
20 | 21 | import javafx.scene.layout.HBox;
|
21 | 22 | import javafx.scene.layout.Priority;
|
| 23 | +import javafx.scene.layout.VBox; |
22 | 24 | import javafx.util.Pair;
|
23 | 25 | import org.apache.commons.lang3.StringUtils;
|
24 | 26 | import org.apache.commons.lang3.tuple.ImmutableTriple;
|
@@ -415,10 +417,12 @@ private synchronized void doAuth(String user, String pass, boolean isAccessToken
|
415 | 417 | case NetUtil.AUTH_ERR:
|
416 | 418 | throw new IOException("The server encountered an error while attempting to authenticate this connection");
|
417 | 419 | default:
|
418 |
| - if (res.startsWith(NetUtil.AUTH_OK)) { |
419 |
| - String accessToken = res.replaceFirst(NetUtil.AUTH_OK + " ", ""); |
| 420 | + if (res.startsWith(NetUtil.AUTH_OK) || res.startsWith(NetUtil.AUTH_RESET)) { |
| 421 | + String accessToken = res.replaceFirst(res.startsWith(NetUtil.AUTH_OK) ? NetUtil.AUTH_OK : NetUtil.AUTH_RESET, "").trim(); |
420 | 422 | Config.ACCESS_TOKEN.setValue(URLDecoder.decode(accessToken, "UTF-8"));
|
421 | 423 | Platform.runLater(() -> Config.USERNAME.setValue(user));
|
| 424 | + if (res.startsWith(NetUtil.AUTH_RESET)) |
| 425 | + throw new PasswordResetRequiredException(); |
422 | 426 | } else
|
423 | 427 | throw new IOException(res);
|
424 | 428 | }
|
@@ -482,6 +486,28 @@ public <T extends Message> void processMessage(T message, ResponseHandler<T> res
|
482 | 486 | } catch (AuthBanException e) {
|
483 | 487 | AssignClient.getInstance().getMainWindow().displayErrorMsg("Temporary Ban", e.getMessage());
|
484 | 488 | responseHandler.onError(false, message);
|
| 489 | + } catch (PasswordResetRequiredException e) { |
| 490 | + AssignClient.getInstance().getMainWindow().displayErrorMsg("Password Reset", e.getMessage(), true); |
| 491 | + disconnect(); |
| 492 | + boolean retry = false; |
| 493 | + do { |
| 494 | + try { |
| 495 | + retry = false; |
| 496 | + resetPassword(); |
| 497 | + responseHandler.onError(true, message); |
| 498 | + } catch (CancellationException e1) { |
| 499 | + responseHandler.onError(false, message); |
| 500 | + } catch (InvalidCredentialsException e1) { |
| 501 | + AssignClient.getInstance().getMainWindow().displayErrorMsg("Incorrect Password", "Your current password is incorrect", true); |
| 502 | + retry = true; |
| 503 | + } catch (WeakPasswordException e1) { |
| 504 | + AssignClient.getInstance().getMainWindow().displayErrorMsg("Weak Password", "Your new password does not meet the complexity requirements", true); |
| 505 | + retry = true; |
| 506 | + } catch (Throwable e1) { |
| 507 | + AssignClient.getInstance().getMainWindow().displayErrorMsg("Error", e1.getMessage()); |
| 508 | + responseHandler.onError(false, message); |
| 509 | + } |
| 510 | + } while (retry); |
485 | 511 | } catch (Throwable e) {
|
486 | 512 | logger.error("Error sending message", e);
|
487 | 513 | responseHandler.onError(false, message);
|
@@ -605,6 +631,74 @@ private Triple<String, String, Boolean> getCredentials() {
|
605 | 631 | return new ImmutableTriple<>(user, pass, isAccessToken);
|
606 | 632 | }
|
607 | 633 |
|
| 634 | + private void resetPassword() throws Exception { |
| 635 | + AtomicReference<Optional<Pair<String, String>>> result = new AtomicReference<>(null); |
| 636 | + Platform.runLater(() -> { |
| 637 | + Dialog<Pair<String, String>> dialog = new Dialog<>(); |
| 638 | + dialog.setTitle("Reset Password"); |
| 639 | + dialog.setHeaderText("Your password has expired.\n" + |
| 640 | + "Please reset your password\n\n" + |
| 641 | + DBUtils.COMPLEXITY_RULES); |
| 642 | + ButtonType loginButtonType = new ButtonType("Reset Password", ButtonBar.ButtonData.OK_DONE); |
| 643 | + dialog.getDialogPane().getButtonTypes().addAll(loginButtonType, ButtonType.CANCEL); |
| 644 | + GridPane grid = new GridPane(); |
| 645 | + grid.setVgap(10); |
| 646 | + grid.setHgap(10); |
| 647 | + grid.setPadding(new Insets(20)); |
| 648 | + PasswordField currentPass = new PasswordField(); |
| 649 | + currentPass.setPromptText("Current Password"); |
| 650 | + PasswordField newPassword = new PasswordField(); |
| 651 | + newPassword.setPromptText("New Password"); |
| 652 | + PasswordField retypePassword = new PasswordField(); |
| 653 | + retypePassword.setPromptText("Retype Password"); |
| 654 | + grid.add(new Label("Current Password:"), 0, 0); |
| 655 | + grid.add(currentPass, 1, 0); |
| 656 | + grid.add(new Label("New Password:"), 0, 1); |
| 657 | + grid.add(newPassword, 1, 1); |
| 658 | + grid.add(new Label("Retype Password:"), 0, 2); |
| 659 | + grid.add(retypePassword, 1, 2); |
| 660 | + Node loginButton = dialog.getDialogPane().lookupButton(loginButtonType); |
| 661 | + loginButton.disableProperty().bind(currentPass.textProperty().isEmpty().or |
| 662 | + (newPassword.textProperty().isEmpty()).or |
| 663 | + (retypePassword.textProperty().isEmpty()).or |
| 664 | + (newPassword.textProperty().isNotEqualTo(retypePassword.textProperty())).or |
| 665 | + (Bindings.createBooleanBinding(() -> !DBUtils.checkPasswordComplexity(Config.USERNAME.getValue(), newPassword.getText()), newPassword.textProperty())).or |
| 666 | + (currentPass.textProperty().isEqualTo(newPassword.textProperty()))); |
| 667 | + dialog.getDialogPane().setContent(grid); |
| 668 | + dialog.setResultConverter(buttonType -> buttonType == ButtonType.CANCEL ? null : new Pair<>(currentPass.getText(), newPassword.getText())); |
| 669 | + currentPass.requestFocus(); |
| 670 | + result.set(dialog.showAndWait()); |
| 671 | + synchronized (result) { |
| 672 | + result.notify(); |
| 673 | + } |
| 674 | + }); |
| 675 | + synchronized (result) { |
| 676 | + try { |
| 677 | + result.wait(); |
| 678 | + } catch (InterruptedException e) { |
| 679 | + e.printStackTrace(); |
| 680 | + } |
| 681 | + } |
| 682 | + if (result.get() != null && result.get().isPresent()) { |
| 683 | + String oldPass = result.get().get().getKey(); |
| 684 | + String newPass = result.get().get().getValue(); |
| 685 | + UserEditMsg editMsg = new UserEditMsg(Config.USERNAME.getValue(), null, newPass, oldPass, true); |
| 686 | + try { |
| 687 | + connect(); |
| 688 | + } catch (PasswordResetRequiredException ignored) { |
| 689 | + } |
| 690 | + Message res; |
| 691 | + try { |
| 692 | + res = editMsg.sendAndGet(this); |
| 693 | + } finally { |
| 694 | + disconnect(); |
| 695 | + } |
| 696 | + if (!(res instanceof UserEditMsg)) |
| 697 | + resetPassword(); |
| 698 | + } else |
| 699 | + throw new CancellationException(); |
| 700 | + } |
| 701 | + |
608 | 702 | public synchronized void disconnect() {
|
609 | 703 | disconnect(true);
|
610 | 704 | }
|
@@ -675,8 +769,14 @@ public synchronized String readMessage() throws IOException {
|
675 | 769 |
|
676 | 770 | @Override
|
677 | 771 | public void handleErrorMsg(ErrorMsg msg) {
|
678 |
| - // TODO: implement |
679 |
| - throw new RuntimeException("Not implemented: \n" + msg); |
| 772 | + switch (msg.getErrorType()) { |
| 773 | + case AUTH_FAIL: |
| 774 | + throw new InvalidCredentialsException(); |
| 775 | + case AUTH_WEAK_PASS: |
| 776 | + throw new WeakPasswordException(); |
| 777 | + default: |
| 778 | + throw new RuntimeException("Error: " + msg.getErrorType()); |
| 779 | + } |
680 | 780 | }
|
681 | 781 |
|
682 | 782 | private boolean showCertWarning() {
|
|
0 commit comments