2
0

Support resetting user's master password.

This commit is contained in:
Maarten Billemont 2018-07-28 21:53:08 -04:00
parent 978b758079
commit 37a7cfa530
9 changed files with 96 additions and 13 deletions

View File

@ -82,11 +82,15 @@ public class MPMasterKey {
Arrays.fill( masterPassword, (char) 0 ); Arrays.fill( masterPassword, (char) 0 );
} }
public boolean isValid() {
return !invalidated;
}
private byte[] masterKey(final MPAlgorithm algorithm) private byte[] masterKey(final MPAlgorithm algorithm)
throws MPKeyUnavailableException, MPAlgorithmException { throws MPKeyUnavailableException, MPAlgorithmException {
Preconditions.checkArgument( masterPassword.length > 0 ); Preconditions.checkArgument( masterPassword.length > 0 );
if (invalidated) if (!isValid())
throw new MPKeyUnavailableException( "Master key was invalidated." ); throw new MPKeyUnavailableException( "Master key was invalidated." );
byte[] masterKey = keyByVersion.get( algorithm.version() ); byte[] masterKey = keyByVersion.get( algorithm.version() );

View File

@ -21,6 +21,7 @@ package com.lyndir.masterpassword.gui.util;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
@ -91,11 +92,21 @@ public abstract class Components {
return new GradientPanel( layout, color ); return new GradientPanel( layout, color );
} }
public static JDialog showDialog(@Nullable final Component owner, @Nullable final String title, final JOptionPane pane) { public static int showDialog(@Nullable final Component owner, @Nullable final String title, final JOptionPane pane) {
JDialog dialog = pane.createDialog( owner, title ); JDialog dialog = pane.createDialog( owner, title );
dialog.setModalityType( Dialog.ModalityType.DOCUMENT_MODAL ); dialog.setModalityType( Dialog.ModalityType.DOCUMENT_MODAL );
showDialog( dialog );
return showDialog( dialog ); Object selectedValue = pane.getValue();
if(selectedValue == null)
return JOptionPane.CLOSED_OPTION;
Object[] options = pane.getOptions();
if(options == null)
return (selectedValue instanceof Integer)? (Integer) selectedValue: JOptionPane.CLOSED_OPTION;
int option = Arrays.binarySearch( options, selectedValue );
return (option < 0)? JOptionPane.CLOSED_OPTION: option;
} }
public static JDialog showDialog(@Nullable final Component owner, @Nullable final String title, final Container content) { public static JDialog showDialog(@Nullable final Component owner, @Nullable final String title, final Container content) {

View File

@ -132,6 +132,10 @@ public abstract class Res {
return icon( "media/icon_lock.png" ); return icon( "media/icon_lock.png" );
} }
public Icon reset() {
return icon( "media/icon_reset.png" );
}
public Icon settings() { public Icon settings() {
return icon( "media/icon_settings.png" ); return icon( "media/icon_settings.png" );
} }

View File

@ -37,8 +37,8 @@ public class UserContentPanel extends JPanel implements FilesPanel.Listener, MPU
private static final Logger logger = Logger.get( UserContentPanel.class ); private static final Logger logger = Logger.get( UserContentPanel.class );
private static final JButton iconButton = Components.button( Res.icons().user(), null, null ); private static final JButton iconButton = Components.button( Res.icons().user(), null, null );
private final JButton addButton = Components.button( Res.icons().add(), event -> addUser(), private final JButton addButton = Components.button( Res.icons().add(), event -> addUser(),
"Add a new user to Master Password." ); "Add a new user to Master Password." );
private final JPanel userToolbar = Components.panel( BoxLayout.PAGE_AXIS ); private final JPanel userToolbar = Components.panel( BoxLayout.PAGE_AXIS );
private final JPanel siteToolbar = Components.panel( BoxLayout.PAGE_AXIS ); private final JPanel siteToolbar = Components.panel( BoxLayout.PAGE_AXIS );
@ -142,6 +142,8 @@ public class UserContentPanel extends JPanel implements FilesPanel.Listener, MPU
private final JButton deleteButton = Components.button( Res.icons().delete(), event -> deleteUser(), private final JButton deleteButton = Components.button( Res.icons().delete(), event -> deleteUser(),
"Delete this user from Master Password." ); "Delete this user from Master Password." );
private final JButton resetButton = Components.button( Res.icons().reset(), event -> resetUser(),
"Change the master password for this user." );
private final JPasswordField masterPasswordField = Components.passwordField(); private final JPasswordField masterPasswordField = Components.passwordField();
private final JLabel errorLabel = Components.label(); private final JLabel errorLabel = Components.label();
@ -156,6 +158,7 @@ public class UserContentPanel extends JPanel implements FilesPanel.Listener, MPU
userToolbar.add( addButton ); userToolbar.add( addButton );
userToolbar.add( deleteButton ); userToolbar.add( deleteButton );
userToolbar.add( resetButton );
add( Components.heading( user.getFullName(), SwingConstants.CENTER ) ); add( Components.heading( user.getFullName(), SwingConstants.CENTER ) );
add( Components.strut() ); add( Components.strut() );
@ -180,12 +183,48 @@ public class UserContentPanel extends JPanel implements FilesPanel.Listener, MPU
return; return;
if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog( if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(
this, strf( "<html>Delete the user <strong>%s</strong>?<br><br><em>%s</em></html>", SwingUtilities.windowForComponent( this ), strf( "<html>Delete the user <strong>%s</strong>?<br><br><em>%s</em></html>",
fileUser.getFullName(), fileUser.getFile().getName() ), fileUser.getFullName(), fileUser.getFile().getName() ),
"Delete User", JOptionPane.YES_NO_OPTION )) "Delete User", JOptionPane.YES_NO_OPTION ))
MPFileUserManager.get().delete( fileUser ); MPFileUserManager.get().delete( fileUser );
} }
private void resetUser() {
JPasswordField passwordField = Components.passwordField();
if (JOptionPane.OK_OPTION == Components.showDialog( this, "Reset User", new JOptionPane( Components.panel(
BoxLayout.PAGE_AXIS,
Components.label( strf( "<html>Enter the new master password for <strong>%s</strong>:</html>",
user.getFullName() ) ),
Components.strut(),
passwordField,
Components.strut(),
Components.label( strf( "<html><em><strong>Note:</strong><br>Changing the master password "
+ "will change all of the user's passwords.<br>"
+ "Changing back to the original master password will also restore<br>"
+ "the user's original passwords.</em></html>",
user.getFullName() ) ) ), JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION ) {
@Override
public void selectInitialValue() {
passwordField.requestFocusInWindow();
}
} )) {
char[] masterPassword = passwordField.getPassword();
if ((masterPassword != null) && (masterPassword.length > 0))
try {
user.reset();
user.authenticate( masterPassword );
}
catch (final MPIncorrectMasterPasswordException e) {
errorLabel.setText( e.getLocalizedMessage() );
throw logger.bug( e );
}
catch (final MPAlgorithmException e) {
logger.err( e, "While resetting master password." );
errorLabel.setText( e.getLocalizedMessage() );
}
}
}
@Override @Override
public void actionPerformed(final ActionEvent event) { public void actionPerformed(final ActionEvent event) {
updateIdenticon(); updateIdenticon();

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -77,8 +77,18 @@ public interface MPUser<S extends MPSite<?>> extends Comparable<MPUser<?>> {
void authenticate(MPMasterKey masterKey) void authenticate(MPMasterKey masterKey)
throws MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException; throws MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException;
/**
* Clear all authentication tokens and secrets from memory, effectively logging the user out.
*/
void invalidate(); void invalidate();
/**
* Wipe the key ID, allowing the user to {@link #authenticate(char[])} with any master password.
*
* Note: Authenticating with a different master password will cause all of the user's results to change.
*/
void reset();
boolean isMasterKeyAvailable(); boolean isMasterKeyAvailable();
@Nonnull @Nonnull

View File

@ -97,12 +97,14 @@ public abstract class MPBasicUser<S extends MPBasicSite<?, ?>> extends Changeabl
@Override @Override
public byte[] getKeyID() { public byte[] getKeyID() {
try { try {
return getMasterKey().getKeyID( getAlgorithm() ); if (isMasterKeyAvailable())
return getMasterKey().getKeyID( getAlgorithm() );
} }
catch (final MPException e) { catch (final MPException e) {
logger.wrn( e, "While deriving key ID for user: %s", this ); logger.wrn( e, "While deriving key ID for user: %s", this );
return null;
} }
return null;
} }
@Nullable @Nullable
@ -143,23 +145,29 @@ public abstract class MPBasicUser<S extends MPBasicSite<?, ?>> extends Changeabl
public void invalidate() { public void invalidate() {
if (masterKey == null) if (masterKey == null)
return; return;
this.masterKey = null; masterKey.invalidate();
masterKey = null;
for (final Listener listener : listeners) for (final Listener listener : listeners)
listener.onUserInvalidated( this ); listener.onUserInvalidated( this );
} }
@Override
public void reset() {
invalidate();
}
@Override @Override
public boolean isMasterKeyAvailable() { public boolean isMasterKeyAvailable() {
return masterKey != null; return (masterKey != null) && masterKey.isValid();
} }
@Nonnull @Nonnull
@Override @Override
public MPMasterKey getMasterKey() public MPMasterKey getMasterKey()
throws MPKeyUnavailableException { throws MPKeyUnavailableException {
if (masterKey == null) if ((masterKey == null) || !masterKey.isValid())
throw new MPKeyUnavailableException( "Master key was not yet set for: " + this ); throw new MPKeyUnavailableException( "Master key was not yet set for: " + this );
return masterKey; return masterKey;

View File

@ -169,6 +169,13 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
} }
} }
@Override
public void reset() {
keyID = null;
super.reset();
}
@Override @Override
public MPFileSite addSite(final String siteName) { public MPFileSite addSite(final String siteName) {
return addSite( new MPFileSite( this, siteName ) ); return addSite( new MPFileSite( this, siteName ) );