2
0

Refactoring masterpassword-algorithm.

This commit is contained in:
Maarten Billemont
2018-05-16 00:12:31 -04:00
parent 8bdf1755b7
commit 0aa7baf59e
24 changed files with 468 additions and 479 deletions

View File

@@ -112,14 +112,11 @@ public class GUI implements UnlockFrame.SignInCallback {
}
protected void open() {
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
if (passwordFrame == null)
unlockFrame.setVisible( true );
else
passwordFrame.setVisible( true );
}
SwingUtilities.invokeLater( () -> {
if (passwordFrame == null)
unlockFrame.setVisible( true );
else
passwordFrame.setVisible( true );
} );
}

View File

@@ -60,15 +60,12 @@ public abstract class Res {
}
public static Future<?> schedule(final Window host, final Runnable job, final long delay, final TimeUnit timeUnit) {
return getExecutor( host ).schedule( new Runnable() {
@Override
public void run() {
try {
job.run();
}
catch (final Throwable t) {
logger.err( t, "Unexpected: %s", t.getLocalizedMessage() );
}
return getExecutor( host ).schedule( () -> {
try {
job.run();
}
catch (final Throwable t) {
logger.err( t, "Unexpected: %s", t.getLocalizedMessage() );
}
}, delay, timeUnit );
}
@@ -79,17 +76,13 @@ public abstract class Res {
public static <V> ListenableFuture<V> schedule(final Window host, final Callable<V> job, final long delay, final TimeUnit timeUnit) {
ScheduledExecutorService executor = getExecutor( host );
return JdkFutureAdapters.listenInPoolThread( executor.schedule( new Callable<V>() {
@Override
public V call()
throws Exception {
try {
return job.call();
}
catch (final Throwable t) {
logger.err( t, "Unexpected: %s", t.getLocalizedMessage() );
throw Throwables.propagate( t );
}
return JdkFutureAdapters.listenInPoolThread( executor.schedule( () -> {
try {
return job.call();
}
catch (final Throwable t) {
logger.err( t, "Unexpected: %s", t.getLocalizedMessage() );
throw Throwables.propagate( t );
}
}, delay, timeUnit ), executor );
}

View File

@@ -31,17 +31,17 @@ import javax.annotation.Nullable;
/**
* @author lhunath, 14-12-16
*/
public class IncognitoSite extends MPBasicSite {
public class MPIncognitoSite extends MPBasicSite {
private final IncognitoUser user;
private final MPIncognitoUser user;
public IncognitoSite(final IncognitoUser user, final String name) {
public MPIncognitoSite(final MPIncognitoUser user, final String name) {
this( user, name, null, null, null, null );
}
public IncognitoSite(final IncognitoUser user, final String name,
@Nullable final MPAlgorithm algorithm, @Nullable final UnsignedInteger counter,
@Nullable final MPResultType resultType, @Nullable final MPResultType loginType) {
public MPIncognitoSite(final MPIncognitoUser user, final String name,
@Nullable final MPAlgorithm algorithm, @Nullable final UnsignedInteger counter,
@Nullable final MPResultType resultType, @Nullable final MPResultType loginType) {
super( name, (algorithm == null)? user.getAlgorithm(): algorithm, counter, resultType, loginType );
this.user = user;

View File

@@ -18,7 +18,7 @@
package com.lyndir.masterpassword.gui.model;
import com.lyndir.masterpassword.MPMasterKey;
import com.lyndir.masterpassword.MPAlgorithm;
import com.lyndir.masterpassword.model.impl.MPBasicUser;
import javax.annotation.Nullable;
@@ -26,10 +26,10 @@ import javax.annotation.Nullable;
/**
* @author lhunath, 2014-06-08
*/
public class IncognitoUser extends MPBasicUser<IncognitoSite> {
public class MPIncognitoUser extends MPBasicUser<MPIncognitoSite> {
public IncognitoUser(final String fullName) {
super(fullName, MPMasterKey.Version.CURRENT.getAlgorithm());
public MPIncognitoUser(final String fullName) {
super( fullName, MPAlgorithm.Version.CURRENT.getAlgorithm());
}
@Nullable

View File

@@ -23,8 +23,8 @@ import com.google.common.primitives.UnsignedInteger;
import com.lyndir.masterpassword.MPAlgorithm;
import com.lyndir.masterpassword.MPResultType;
import com.lyndir.masterpassword.gui.Res;
import com.lyndir.masterpassword.gui.model.IncognitoSite;
import com.lyndir.masterpassword.gui.model.IncognitoUser;
import com.lyndir.masterpassword.gui.model.MPIncognitoSite;
import com.lyndir.masterpassword.gui.model.MPIncognitoUser;
import com.lyndir.masterpassword.gui.util.Components;
import java.awt.*;
import java.awt.event.ActionEvent;
@@ -40,7 +40,7 @@ import javax.swing.event.DocumentListener;
* @author lhunath, 2014-06-11
*/
@SuppressWarnings({ "serial", "MagicNumber" })
public class IncognitoAuthenticationPanel extends AuthenticationPanel<IncognitoUser> implements DocumentListener, ActionListener {
public class IncognitoAuthenticationPanel extends AuthenticationPanel<MPIncognitoUser> implements DocumentListener, ActionListener {
private final JTextField fullNameField;
private final JPasswordField masterPasswordField;
@@ -82,20 +82,20 @@ public class IncognitoAuthenticationPanel extends AuthenticationPanel<IncognitoU
}
@Override
public PasswordFrame<IncognitoUser, ?> newPasswordFrame() {
return new PasswordFrame<IncognitoUser, IncognitoSite>( Preconditions.checkNotNull( getSelectedUser() ) ) {
public PasswordFrame<MPIncognitoUser, ?> newPasswordFrame() {
return new PasswordFrame<MPIncognitoUser, MPIncognitoSite>( Preconditions.checkNotNull( getSelectedUser() ) ) {
@Override
protected IncognitoSite createSite(final IncognitoUser user, final String siteName, final UnsignedInteger siteCounter,
final MPResultType resultType, final MPAlgorithm algorithm) {
return new IncognitoSite( user, siteName, algorithm, siteCounter, resultType, null );
protected MPIncognitoSite createSite(final MPIncognitoUser user, final String siteName, final UnsignedInteger siteCounter,
final MPResultType resultType, final MPAlgorithm algorithm) {
return new MPIncognitoSite( user, siteName, algorithm, siteCounter, resultType, null );
}
};
}
@Nullable
@Override
protected IncognitoUser getSelectedUser() {
return new IncognitoUser( fullNameField.getText() );
protected MPIncognitoUser getSelectedUser() {
return new MPIncognitoUser( fullNameField.getText() );
}
@Nonnull

View File

@@ -21,8 +21,6 @@ package com.lyndir.masterpassword.gui.view;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.common.primitives.UnsignedInteger;
import com.google.common.util.concurrent.*;
@@ -30,19 +28,19 @@ import com.lyndir.masterpassword.*;
import com.lyndir.masterpassword.gui.Res;
import com.lyndir.masterpassword.gui.util.Components;
import com.lyndir.masterpassword.gui.util.UnsignedIntegerModel;
import com.lyndir.masterpassword.model.*;
import com.lyndir.masterpassword.model.impl.MPBasicSite;
import com.lyndir.masterpassword.model.impl.MPFileSite;
import com.lyndir.masterpassword.model.impl.MPFileUser;
import com.lyndir.masterpassword.model.MPUser;
import com.lyndir.masterpassword.model.impl.*;
import java.awt.*;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.event.*;
import java.util.concurrent.Callable;
import java.util.Collection;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
/**
@@ -54,7 +52,7 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPBasicSite>
private final Components.GradientPanel root;
private final JTextField siteNameField;
private final JButton siteActionButton;
private final JComboBox<MPMasterKey.Version> siteVersionField;
private final JComboBox<MPAlgorithm.Version> siteVersionField;
private final JSpinner siteCounterField;
private final UnsignedIntegerModel siteCounterModel;
private final JComboBox<MPResultType> resultTypeField;
@@ -105,14 +103,11 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPBasicSite>
Transferable clipboardContents = new StringSelection( sitePassword );
Toolkit.getDefaultToolkit().getSystemClipboard().setContents( clipboardContents, null );
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
passwordField.setText( null );
siteNameField.setText( null );
SwingUtilities.invokeLater( () -> {
passwordField.setText( null );
siteNameField.setText( null );
dispatchEvent( new WindowEvent( PasswordFrame.this, WindowEvent.WINDOW_CLOSING ) );
}
dispatchEvent( new WindowEvent( PasswordFrame.this, WindowEvent.WINDOW_CLOSING ) );
} );
}
@@ -122,19 +117,16 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPBasicSite>
} );
}
} );
siteActionButton.addActionListener( new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
if (currentSite == null)
return;
if (currentSite instanceof MPFileSite)
PasswordFrame.this.user.deleteSite( currentSite );
else
PasswordFrame.this.user.addSite( currentSite );
siteNameField.requestFocus();
siteActionButton.addActionListener( e -> {
if (currentSite == null)
return;
if (currentSite instanceof MPFileSite)
this.user.deleteSite( currentSite );
else
this.user.addSite( currentSite );
siteNameField.requestFocus();
updatePassword( true );
}
updatePassword( true );
} );
sitePanel.add( siteControls );
sitePanel.add( Components.stud() );
@@ -145,48 +137,28 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPBasicSite>
JComponent siteSettings = Components.boxLayout( BoxLayout.LINE_AXIS, //
resultTypeField = Components.comboBox( types ), //
Components.stud(), //
siteVersionField = Components.comboBox( MPMasterKey.Version.values() ), //
siteVersionField = Components.comboBox( MPAlgorithm.Version.values() ), //
Components.stud(), //
siteCounterField = Components.spinner( siteCounterModel ) );
sitePanel.add( siteSettings );
resultTypeField.setFont( Res.valueFont().deriveFont( resultTypeField.getFont().getSize2D() ) );
resultTypeField.setSelectedItem( user.getAlgorithm().mpw_default_result_type() );
resultTypeField.addItemListener( new ItemListener() {
@Override
public void itemStateChanged(final ItemEvent e) {
updatePassword( true );
}
} );
resultTypeField.addItemListener( e -> updatePassword( true ) );
siteVersionField.setFont( Res.valueFont().deriveFont( siteVersionField.getFont().getSize2D() ) );
siteVersionField.setAlignmentX( RIGHT_ALIGNMENT );
siteVersionField.setSelectedItem( user.getAlgorithm() );
siteVersionField.addItemListener( new ItemListener() {
@Override
public void itemStateChanged(final ItemEvent e) {
updatePassword( true );
}
} );
siteVersionField.addItemListener( e -> updatePassword( true ) );
siteCounterField.setFont( Res.valueFont().deriveFont( siteCounterField.getFont().getSize2D() ) );
siteCounterField.setAlignmentX( RIGHT_ALIGNMENT );
siteCounterField.addChangeListener( new ChangeListener() {
@Override
public void stateChanged(final ChangeEvent e) {
updatePassword( true );
}
} );
siteCounterField.addChangeListener( e -> updatePassword( true ) );
// Mask
maskPasswordField = Components.checkBox( "Hide Password" );
maskPasswordField.setAlignmentX( Component.CENTER_ALIGNMENT );
maskPasswordField.setSelected( true );
maskPasswordField.addItemListener( new ItemListener() {
@Override
public void itemStateChanged(final ItemEvent e) {
updateMask();
}
} );
maskPasswordField.addItemListener( e -> updateMask() );
// Password
passwordField = Components.passwordField();
@@ -229,7 +201,7 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPBasicSite>
@Nonnull
private ListenableFuture<String> updatePassword(final boolean allowNameCompletion) {
final String siteNameQuery = siteNameField.getText();
String siteNameQuery = siteNameField.getText();
if (updatingUI)
return Futures.immediateCancelledFuture();
if ((siteNameQuery == null) || siteNameQuery.isEmpty() || !user.isMasterKeyAvailable()) {
@@ -243,53 +215,40 @@ public abstract class PasswordFrame<U extends MPUser<S>, S extends MPBasicSite>
MPAlgorithm siteAlgorithm = siteVersionField.getItemAt( siteVersionField.getSelectedIndex() ).getAlgorithm();
UnsignedInteger siteCounter = siteCounterModel.getNumber();
Iterable<S> siteResults = user.findSites( siteNameQuery );
Collection<S> siteResults = user.findSites( siteNameQuery );
if (!allowNameCompletion)
siteResults = FluentIterable.from( siteResults ).filter( new Predicate<S>() {
@Override
public boolean apply(@Nullable final S siteResult) {
return (siteResult != null) && siteNameQuery.equals( siteResult.getName() );
}
} );
final S site = ifNotNullElse( Iterables.getFirst( siteResults, null ),
createSite( user, siteNameQuery, siteCounter, resultType, siteAlgorithm ) );
siteResults = siteResults.stream().filter(
siteResult -> (siteResult != null) && siteNameQuery.equals( siteResult.getName() ) ).collect( Collectors.toList() );
S site = ifNotNullElse( Iterables.getFirst( siteResults, null ),
createSite( user, siteNameQuery, siteCounter, resultType, siteAlgorithm ) );
if ((currentSite != null) && currentSite.getName().equals( site.getName() )) {
site.setResultType( resultType );
site.setAlgorithm( siteAlgorithm );
site.setCounter( siteCounter );
}
ListenableFuture<String> passwordFuture = Res.execute( this, new Callable<String>() {
@Override
public String call()
throws Exception {
return site.getResult( MPKeyPurpose.Authentication, null, null );
}
} );
ListenableFuture<String> passwordFuture = Res.execute( this, () -> site.getResult( MPKeyPurpose.Authentication, null, null ) );
Futures.addCallback( passwordFuture, new FutureCallback<String>() {
@Override
public void onSuccess(@Nullable final String sitePassword) {
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
updatingUI = true;
currentSite = site;
siteActionButton.setVisible( user instanceof MPFileUser );
if (currentSite instanceof MPFileSite)
siteActionButton.setText( "Delete Site" );
else
siteActionButton.setText( "Add Site" );
resultTypeField.setSelectedItem( currentSite.getResultType() );
siteVersionField.setSelectedItem( currentSite.getAlgorithm() );
siteCounterField.setValue( currentSite.getCounter() );
siteNameField.setText( currentSite.getName() );
if (siteNameField.getText().startsWith( siteNameQuery ))
siteNameField.select( siteNameQuery.length(), siteNameField.getText().length() );
SwingUtilities.invokeLater( () -> {
updatingUI = true;
currentSite = site;
siteActionButton.setVisible( user instanceof MPFileUser );
if (currentSite instanceof MPFileSite)
siteActionButton.setText( "Delete Site" );
else
siteActionButton.setText( "Add Site" );
resultTypeField.setSelectedItem( currentSite.getResultType() );
siteVersionField.setSelectedItem( currentSite.getAlgorithm() );
siteCounterField.setValue( currentSite.getCounter() );
siteNameField.setText( currentSite.getName() );
if (siteNameField.getText().startsWith( siteNameQuery ))
siteNameField.select( siteNameQuery.length(), siteNameField.getText().length() );
passwordField.setText( sitePassword );
tipLabel.setText( "Press [Enter] to copy the password. Then paste it into the password field." );
updatingUI = false;
}
passwordField.setText( sitePassword );
tipLabel.setText( "Press [Enter] to copy the password. Then paste it into the password field." );
updatingUI = false;
} );
}

View File

@@ -119,20 +119,12 @@ public class UnlockFrame extends JFrame {
authenticationContainer.add( authenticationPanel );
authenticationContainer.add( Components.stud() );
final JCheckBox incognitoCheckBox = Components.checkBox( "Incognito" );
JCheckBox incognitoCheckBox = Components.checkBox( "Incognito" );
incognitoCheckBox.setToolTipText( "Log in without saving any information." );
incognitoCheckBox.setSelected( incognito );
incognitoCheckBox.addItemListener( new ItemListener() {
@Override
public void itemStateChanged(final ItemEvent e) {
incognito = incognitoCheckBox.isSelected();
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
createAuthenticationPanel();
}
} );
}
incognitoCheckBox.addItemListener( e -> {
incognito = incognitoCheckBox.isSelected();
SwingUtilities.invokeLater( this::createAuthenticationPanel );
} );
JComponent toolsPanel = Components.boxLayout( BoxLayout.LINE_AXIS, incognitoCheckBox, Box.createGlue() );
@@ -149,12 +141,7 @@ public class UnlockFrame extends JFrame {
validate();
repack();
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
ifNotNullElse( authenticationPanel.getFocusComponent(), signInButton ).requestFocusInWindow();
}
} );
SwingUtilities.invokeLater( () -> ifNotNullElse( authenticationPanel.getFocusComponent(), signInButton ).requestFocusInWindow() );
}
void updateUser(@Nullable final MPUser<? extends MPSite> user) {
@@ -165,28 +152,20 @@ public class UnlockFrame extends JFrame {
boolean checkSignIn() {
if (identiconFuture != null)
identiconFuture.cancel( false );
identiconFuture = Res.schedule( this, new Runnable() {
@Override
public void run() {
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
String fullName = (user == null)? "": user.getFullName();
char[] masterPassword = authenticationPanel.getMasterPassword();
identiconFuture = Res.schedule( this, () -> SwingUtilities.invokeLater( () -> {
String fullName = (user == null)? "": user.getFullName();
char[] masterPassword = authenticationPanel.getMasterPassword();
if (fullName.isEmpty() || (masterPassword.length == 0)) {
identiconLabel.setText( " " );
return;
}
MPIdenticon identicon = new MPIdenticon( fullName, masterPassword );
identiconLabel.setText( identicon.getText() );
identiconLabel.setForeground(
Res.colors().fromIdenticonColor( identicon.getColor(), Res.Colors.BackgroundMode.DARK ) );
}
} );
if (fullName.isEmpty() || (masterPassword.length == 0)) {
identiconLabel.setText( " " );
return;
}
}, 300, TimeUnit.MILLISECONDS );
MPIdenticon identicon = new MPIdenticon( fullName, masterPassword );
identiconLabel.setText( identicon.getText() );
identiconLabel.setForeground(
Res.colors().fromIdenticonColor( identicon.getColor(), Res.Colors.BackgroundMode.DARK ) );
} ), 300, TimeUnit.MILLISECONDS );
String fullName = (user == null)? "": user.getFullName();
char[] masterPassword = authenticationPanel.getMasterPassword();
@@ -206,37 +185,29 @@ public class UnlockFrame extends JFrame {
signInButton.setEnabled( false );
signInButton.setText( "Signing In..." );
Res.execute( this, new Runnable() {
@Override
public void run() {
try {
user.authenticate( authenticationPanel.getMasterPassword() );
Res.execute( this, () -> {
try {
user.authenticate( authenticationPanel.getMasterPassword() );
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
signInCallback.signedIn( authenticationPanel.newPasswordFrame() );
dispose();
}
} );
}
catch (final MPIncorrectMasterPasswordException e) {
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
JOptionPane.showMessageDialog( null, e.getLocalizedMessage(), "Sign In Failed", JOptionPane.ERROR_MESSAGE );
authenticationPanel.reset();
signInButton.setText( "Sign In" );
for (final JComponent signInComponent : signInComponents)
signInComponent.setEnabled( true );
checkSignIn();
}
} );
}
SwingUtilities.invokeLater( () -> {
signInCallback.signedIn( authenticationPanel.newPasswordFrame() );
dispose();
} );
}
catch (final MPIncorrectMasterPasswordException e) {
SwingUtilities.invokeLater( () -> {
JOptionPane.showMessageDialog( null, e.getLocalizedMessage(), "Sign In Failed", JOptionPane.ERROR_MESSAGE );
authenticationPanel.reset();
signInButton.setText( "Sign In" );
for (final JComponent signInComponent : signInComponents)
signInComponent.setEnabled( true );
checkSignIn();
} );
}
} );
}
@FunctionalInterface
public interface SignInCallback {
void signedIn(PasswordFrame<?, ?> passwordFrame);