From a6ab9b91944393ed61342e7cd38f8cafa591004a Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Wed, 4 Feb 2015 19:51:38 -0500 Subject: [PATCH] Moar UI work on the Java app + support for per-site algorithm versioning. --- .../com/lyndir/masterpassword/MasterKey.java | 5 + .../lyndir/masterpassword/MasterKeyV0.java | 4 +- .../lyndir/masterpassword/MasterKeyV2.java | 2 +- .../lyndir/masterpassword/MasterKeyV3.java | 2 +- .../gui/AuthenticationPanel.java | 4 +- .../com/lyndir/masterpassword/gui/GUI.java | 2 +- .../gui/IncognitoAuthenticationPanel.java | 8 -- .../masterpassword/gui/IncognitoSite.java | 22 +++- .../masterpassword/gui/IncognitoUser.java | 12 +- .../gui/ModelAuthenticationPanel.java | 15 +-- .../lyndir/masterpassword/gui/ModelSite.java | 14 +++ .../lyndir/masterpassword/gui/ModelUser.java | 6 + .../masterpassword/gui/PasswordFrame.java | 103 ++++++++--------- .../com/lyndir/masterpassword/gui/Site.java | 5 + .../masterpassword/gui/UnlockFrame.java | 26 ++--- .../com/lyndir/masterpassword/gui/User.java | 37 +++--- .../masterpassword/util/Components.java | 105 +++++++++++++++++- .../lyndir/masterpassword/model/MPSite.java | 17 ++- .../model/MPSiteMarshaller.java | 2 +- 19 files changed, 262 insertions(+), 129 deletions(-) diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java index 981fc590..d2325c37 100644 --- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java +++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java @@ -28,6 +28,7 @@ public abstract class MasterKey { return create( Version.CURRENT, fullName, masterPassword ); } + @Nonnull public static MasterKey create(Version version, final String fullName, final String masterPassword) { switch (version) { @@ -75,6 +76,10 @@ public abstract class MasterKey { public abstract String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant, @Nullable final String siteContext); + public boolean isValid() { + return masterKey != null; + } + public void invalidate() { if (masterKey != null) { diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java index a55614df..8e0b1c77 100644 --- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java +++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java @@ -100,13 +100,13 @@ public class MasterKeyV0 extends MasterKey { logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) ); Preconditions.checkState( sitePasswordSeed.length > 0 ); - int templateIndex = sitePasswordSeed[0]; + int templateIndex = sitePasswordSeed[0] & 0xFFFF; MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex ); logger.trc( "type %s, template: %s", siteType, template.getTemplateString() ); StringBuilder password = new StringBuilder( template.length() ); for (int i = 0; i < template.length(); ++i) { - int characterIndex = sitePasswordSeed[i + 1]; + int characterIndex = sitePasswordSeed[i + 1] & 0xFFFF; MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i ); char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex ); logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex, diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java index 6d414938..5ebc0012 100644 --- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java +++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java @@ -13,7 +13,7 @@ import javax.annotation.Nullable; * * @author lhunath, 2014-08-30 */ -public class MasterKeyV2 extends MasterKeyV0 { +public class MasterKeyV2 extends MasterKeyV1 { @SuppressWarnings("UnusedDeclaration") private static final Logger logger = Logger.get( MasterKeyV2.class ); diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV3.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV3.java index a4ec51df..577c57f9 100644 --- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV3.java +++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV3.java @@ -14,7 +14,7 @@ import javax.annotation.Nullable; * * @author lhunath, 2014-08-30 */ -public class MasterKeyV3 extends MasterKeyV0 { +public class MasterKeyV3 extends MasterKeyV2 { @SuppressWarnings("UnusedDeclaration") private static final Logger logger = Logger.get( MasterKeyV3.class ); diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/AuthenticationPanel.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/AuthenticationPanel.java index bfb36895..a4da41ce 100644 --- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/AuthenticationPanel.java +++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/AuthenticationPanel.java @@ -1,6 +1,7 @@ package com.lyndir.masterpassword.gui; import com.google.common.collect.ImmutableList; +import com.lyndir.masterpassword.util.Components; import java.awt.*; import javax.swing.*; @@ -8,12 +9,13 @@ import javax.swing.*; /** * @author lhunath, 2014-06-11 */ -public abstract class AuthenticationPanel extends JPanel { +public abstract class AuthenticationPanel extends Components.GradientPanel { protected final UnlockFrame unlockFrame; protected final JLabel avatarLabel; public AuthenticationPanel(final UnlockFrame unlockFrame) { + super( null, null ); this.unlockFrame = unlockFrame; setLayout( new BoxLayout( this, BoxLayout.PAGE_AXIS ) ); diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/GUI.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/GUI.java index 6be574c4..bcfa5412 100644 --- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/GUI.java +++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/GUI.java @@ -94,7 +94,7 @@ public class GUI implements UnlockFrame.SignInCallback { @Override public boolean signedIn(final User user) { - if (!user.hasKey()) + if (!user.isKeyAvailable()) return false; try { user.getKey(); diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoAuthenticationPanel.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoAuthenticationPanel.java index bdb4dfa7..928e6d9d 100644 --- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoAuthenticationPanel.java +++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoAuthenticationPanel.java @@ -24,14 +24,10 @@ public class IncognitoAuthenticationPanel extends AuthenticationPanel implements add( Components.stud() ); JLabel fullNameLabel = Components.label( "Full Name:" ); - fullNameLabel.setAlignmentX( LEFT_ALIGNMENT ); - fullNameLabel.setHorizontalAlignment( SwingConstants.CENTER ); - fullNameLabel.setVerticalAlignment( SwingConstants.BOTTOM ); add( fullNameLabel ); fullNameField = Components.textField(); fullNameField.setFont( Res.valueFont().deriveFont( 12f ) ); - fullNameField.setAlignmentX( LEFT_ALIGNMENT ); fullNameField.getDocument().addDocumentListener( this ); fullNameField.addActionListener( this ); add( fullNameField ); @@ -39,13 +35,9 @@ public class IncognitoAuthenticationPanel extends AuthenticationPanel implements // Master Password JLabel masterPasswordLabel = Components.label( "Master Password:" ); - masterPasswordLabel.setAlignmentX( LEFT_ALIGNMENT ); - masterPasswordLabel.setHorizontalAlignment( SwingConstants.CENTER ); - masterPasswordLabel.setVerticalAlignment( SwingConstants.BOTTOM ); add( masterPasswordLabel ); masterPasswordField = Components.passwordField(); - masterPasswordField.setAlignmentX( LEFT_ALIGNMENT ); masterPasswordField.addActionListener( this ); masterPasswordField.getDocument().addDocumentListener( this ); add( masterPasswordField ); diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoSite.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoSite.java index c6671b70..9e018ac1 100644 --- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoSite.java +++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoSite.java @@ -1,6 +1,7 @@ package com.lyndir.masterpassword.gui; import com.lyndir.masterpassword.MPSiteType; +import com.lyndir.masterpassword.MasterKey; /** @@ -8,14 +9,17 @@ import com.lyndir.masterpassword.MPSiteType; */ public class IncognitoSite extends Site { - private String siteName; - private MPSiteType siteType; - private int siteCounter; + private String siteName; + private MPSiteType siteType; + private int siteCounter; + private MasterKey.Version algorithmVersion; - public IncognitoSite(final String siteName, final MPSiteType siteType, final int siteCounter) { + public IncognitoSite(final String siteName, final MPSiteType siteType, final int siteCounter, + final MasterKey.Version algorithmVersion) { this.siteName = siteName; this.siteType = siteType; this.siteCounter = siteCounter; + this.algorithmVersion = algorithmVersion; } public String getSiteName() { @@ -34,6 +38,16 @@ public class IncognitoSite extends Site { this.siteType = siteType; } + @Override + public MasterKey.Version getAlgorithmVersion() { + return algorithmVersion; + } + + @Override + public void setAlgorithmVersion(final MasterKey.Version algorithmVersion) { + this.algorithmVersion = algorithmVersion; + } + public int getSiteCounter() { return siteCounter; } diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoUser.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoUser.java index bd71ef8d..366938e6 100644 --- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoUser.java +++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/IncognitoUser.java @@ -9,8 +9,9 @@ import com.lyndir.masterpassword.MasterKey; */ public class IncognitoUser extends User { - private final String fullName; - private final String masterPassword; + private final String fullName; + private final String masterPassword; + private MasterKey.Version algorithmVersion; public IncognitoUser(final String fullName, final String masterPassword) { this.fullName = fullName; @@ -28,7 +29,12 @@ public class IncognitoUser extends User { @Override public MasterKey.Version getAlgorithmVersion() { - return MasterKey.Version.CURRENT; + return algorithmVersion; + } + + @Override + public void setAlgorithmVersion(final MasterKey.Version algorithmVersion) { + this.algorithmVersion = algorithmVersion; } @Override diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelAuthenticationPanel.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelAuthenticationPanel.java index 4a92a8a1..12a528a7 100644 --- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelAuthenticationPanel.java +++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelAuthenticationPanel.java @@ -44,19 +44,10 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite // User JLabel userLabel = Components.label( "User:" ); - userLabel.setAlignmentX( LEFT_ALIGNMENT ); - userLabel.setHorizontalAlignment( SwingConstants.CENTER ); - userLabel.setVerticalAlignment( SwingConstants.BOTTOM ); add( userLabel ); - userField = new JComboBox( new DefaultComboBoxModel<>( readConfigUsers() ) ) { - @Override - public Dimension getMaximumSize() { - return new Dimension( Integer.MAX_VALUE, getPreferredSize().height ); - } - }; + userField = Components.comboBox( readConfigUsers() ); userField.setFont( Res.valueFont().deriveFont( 12f ) ); - userField.setAlignmentX( LEFT_ALIGNMENT ); userField.addItemListener( this ); userField.addActionListener( this ); add( userField ); @@ -64,13 +55,9 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite // Master Password masterPasswordLabel = Components.label( "Master Password:" ); - masterPasswordLabel.setAlignmentX( LEFT_ALIGNMENT ); - masterPasswordLabel.setHorizontalAlignment( SwingConstants.CENTER ); - masterPasswordLabel.setVerticalAlignment( SwingConstants.BOTTOM ); add( masterPasswordLabel ); masterPasswordField = Components.passwordField(); - masterPasswordField.setAlignmentX( LEFT_ALIGNMENT ); masterPasswordField.addActionListener( this ); masterPasswordField.getDocument().addDocumentListener( this ); add( masterPasswordField ); diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelSite.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelSite.java index 1a4f9d85..c49a054d 100644 --- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelSite.java +++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelSite.java @@ -1,6 +1,7 @@ package com.lyndir.masterpassword.gui; import com.lyndir.masterpassword.MPSiteType; +import com.lyndir.masterpassword.MasterKey; import com.lyndir.masterpassword.model.*; @@ -37,6 +38,19 @@ public class ModelSite extends Site { } } + @Override + public MasterKey.Version getAlgorithmVersion() { + return model.getAlgorithmVersion(); + } + + @Override + public void setAlgorithmVersion(final MasterKey.Version algorithmVersion) { + if (algorithmVersion != getAlgorithmVersion()) { + model.setAlgorithmVersion( algorithmVersion ); + MPUserFileManager.get().save(); + } + } + public int getSiteCounter() { return model.getSiteCounter(); } diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelUser.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelUser.java index a2915b53..24b29aeb 100644 --- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelUser.java +++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelUser.java @@ -42,6 +42,12 @@ public class ModelUser extends User { return model.getAlgorithmVersion(); } + @Override + public void setAlgorithmVersion(final MasterKey.Version algorithmVersion) { + model.setAlgorithmVersion( algorithmVersion ); + MPUserFileManager.get().save(); + } + @Override public int getAvatar() { return model.getAvatar(); diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/PasswordFrame.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/PasswordFrame.java index 667ab7f3..e79793c5 100644 --- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/PasswordFrame.java +++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/PasswordFrame.java @@ -20,52 +20,46 @@ import javax.swing.event.*; */ public class PasswordFrame extends JFrame implements DocumentListener { - private final User user; - private final JTextField siteNameField; - private final JButton siteAddButton; - private final JComboBox siteTypeField; - private final JSpinner siteCounterField; - private final JPasswordField passwordField; - private final JLabel tipLabel; - private final JCheckBox maskPasswordField; - private final char passwordEchoChar; - private final Font passwordEchoFont; - private boolean updatingUI; - private Site currentSite; + private final User user; + private final Components.GradientPanel root; + private final JTextField siteNameField; + private final JButton siteAddButton; + private final JComboBox siteTypeField; + private final JComboBox siteVersionField; + private final JSpinner siteCounterField; + private final JPasswordField passwordField; + private final JLabel tipLabel; + private final JCheckBox maskPasswordField; + private final char passwordEchoChar; + private final Font passwordEchoFont; + private boolean updatingUI; + private Site currentSite; public PasswordFrame(User user) throws HeadlessException { super( "Master Password" ); this.user = user; - JLabel label; - setDefaultCloseOperation( DISPOSE_ON_CLOSE ); - setContentPane( new JPanel( new BorderLayout( 20, 20 ) ) { - { - setBackground( Res.colors().frameBg() ); - setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) ); - } - } ); - - // User - add( label = Components.label( strf( "Generating passwords for: %s", user.getFullName() ) ), BorderLayout.NORTH ); - label.setAlignmentX( LEFT_ALIGNMENT ); + setContentPane( root = Components.gradientPanel( new BorderLayout( 20, 20 ), Res.colors().frameBg() ) ); + root.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) ); // Site JPanel sitePanel = Components.boxLayout( BoxLayout.PAGE_AXIS ); + sitePanel.setOpaque( true ); sitePanel.setBackground( Res.colors().controlBg() ); sitePanel.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) ); add( Components.bordered( sitePanel, BorderFactory.createRaisedBevelBorder(), Res.colors().frameBg() ), BorderLayout.CENTER ); - // Site Name - sitePanel.add( label = Components.label( "Site Name:" ) ); - label.setAlignmentX( LEFT_ALIGNMENT ); + // User + sitePanel.add( Components.label( strf( "Generating passwords for: %s", user.getFullName() ), JLabel.CENTER ) ); + sitePanel.add( Components.stud() ); + // Site Name + sitePanel.add( Components.label( "Site Name:" ) ); JComponent siteControls = Components.boxLayout( BoxLayout.LINE_AXIS, // siteNameField = Components.textField(), Components.stud(), siteAddButton = Components.button( "Add Site" ) ); - siteNameField.setAlignmentX( LEFT_ALIGNMENT ); siteNameField.getDocument().addDocumentListener( this ); siteNameField.addActionListener( new ActionListener() { @Override @@ -94,8 +88,6 @@ public class PasswordFrame extends JFrame implements DocumentListener { } } ); siteAddButton.setVisible( false ); - siteAddButton.setAlignmentX( RIGHT_ALIGNMENT ); - siteAddButton.setAlignmentY( CENTER_ALIGNMENT ); siteAddButton.addActionListener( new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { @@ -103,23 +95,20 @@ public class PasswordFrame extends JFrame implements DocumentListener { siteAddButton.setVisible( false ); } } ); - siteControls.setBackground( null ); - siteControls.setAlignmentX( LEFT_ALIGNMENT ); sitePanel.add( siteControls ); + sitePanel.add( Components.stud() ); // Site Type & Counter MPSiteType[] types = Iterables.toArray( MPSiteType.forClass( MPSiteTypeClass.Generated ), MPSiteType.class ); JComponent siteSettings = Components.boxLayout( BoxLayout.LINE_AXIS, // - siteTypeField = new JComboBox<>( types ), // + siteTypeField = Components.comboBox( types ), // + Components.stud(), // + siteVersionField = Components.comboBox( MasterKey.Version.values() ), // Components.stud(), // siteCounterField = Components.spinner( new SpinnerNumberModel( 1, 1, Integer.MAX_VALUE, 1 ) ) ); - siteSettings.setBackground( null ); - siteSettings.setAlignmentX( LEFT_ALIGNMENT ); sitePanel.add( siteSettings ); siteTypeField.setFont( Res.valueFont().deriveFont( 12f ) ); - siteTypeField.setAlignmentX( LEFT_ALIGNMENT ); - siteTypeField.setAlignmentY( CENTER_ALIGNMENT ); siteTypeField.setSelectedItem( MPSiteType.GeneratedLong ); siteTypeField.addItemListener( new ItemListener() { @Override @@ -128,9 +117,18 @@ public class PasswordFrame extends JFrame implements DocumentListener { } } ); + siteVersionField.setFont( Res.valueFont().deriveFont( 12f ) ); + siteVersionField.setAlignmentX( RIGHT_ALIGNMENT ); + siteVersionField.setSelectedItem( user.getAlgorithmVersion() ); + siteVersionField.addItemListener( new ItemListener() { + @Override + public void itemStateChanged(final ItemEvent e) { + updatePassword(); + } + } ); + siteCounterField.setFont( Res.valueFont().deriveFont( 12f ) ); siteCounterField.setAlignmentX( RIGHT_ALIGNMENT ); - siteCounterField.setAlignmentY( CENTER_ALIGNMENT ); siteCounterField.addChangeListener( new ChangeListener() { @Override public void stateChanged(final ChangeEvent e) { @@ -151,9 +149,11 @@ public class PasswordFrame extends JFrame implements DocumentListener { // Password passwordField = new JPasswordField(); + passwordField.setAlignmentX( Component.CENTER_ALIGNMENT ); passwordField.setEditable( false ); passwordField.setHorizontalAlignment( JTextField.CENTER ); passwordField.putClientProperty( "JPasswordField.cutCopyAllowed", true ); + passwordField.setBorder( null ); passwordEchoChar = passwordField.getEchoChar(); passwordEchoFont = passwordField.getFont().deriveFont( 40f ); updateMask(); @@ -161,16 +161,15 @@ public class PasswordFrame extends JFrame implements DocumentListener { // Tip tipLabel = Components.label( " ", JLabel.CENTER ); tipLabel.setAlignmentX( Component.CENTER_ALIGNMENT ); - - JPanel passwordContainer = Components.boxLayout( BoxLayout.PAGE_AXIS, maskPasswordField, - Components.bordered( passwordField, BorderFactory.createLoweredSoftBevelBorder(), - Res.colors().frameBg() ), tipLabel ); - passwordContainer.setBackground( null ); - add( passwordContainer, BorderLayout.SOUTH ); + JPanel passwordContainer = Components.boxLayout( BoxLayout.PAGE_AXIS, maskPasswordField, passwordField, tipLabel ); + passwordContainer.setOpaque( true ); + passwordContainer.setBackground( Color.white ); + passwordContainer.setBorder( BorderFactory.createEmptyBorder( 8, 8, 8, 8 ) ); + add( Components.bordered( passwordContainer, BorderFactory.createLoweredSoftBevelBorder(), Res.colors().frameBg() ), + BorderLayout.SOUTH ); pack(); - setMinimumSize( getSize() ); - setPreferredSize( new Dimension( 600, getSize().height ) ); + setMinimumSize( new Dimension( Math.max( 600, getPreferredSize().width ), Math.max( 300, getPreferredSize().height ) ) ); pack(); setLocationByPlatform( true ); @@ -188,19 +187,21 @@ public class PasswordFrame extends JFrame implements DocumentListener { final String siteNameQuery = siteNameField.getText(); if (updatingUI) return Futures.immediateCancelledFuture(); - if (siteNameQuery == null || siteNameQuery.isEmpty() || !user.hasKey()) { + if (siteNameQuery == null || siteNameQuery.isEmpty() || !user.isKeyAvailable()) { tipLabel.setText( null ); passwordField.setText( null ); return Futures.immediateCancelledFuture(); } - MPSiteType siteType = siteTypeField.getModel().getElementAt( siteTypeField.getSelectedIndex() ); + final MPSiteType siteType = siteTypeField.getModel().getElementAt( siteTypeField.getSelectedIndex() ); + final MasterKey.Version siteVersion = siteVersionField.getItemAt( siteVersionField.getSelectedIndex() ); final int siteCounter = (Integer) siteCounterField.getValue(); final Site site = currentSite != null && currentSite.getSiteName().equals( siteNameQuery )? currentSite - : Iterables.getFirst( user.findSitesByName( siteNameQuery ), new IncognitoSite( siteNameQuery, siteType, siteCounter ) ); + : Iterables.getFirst( user.findSitesByName( siteNameQuery ), new IncognitoSite( siteNameQuery, siteType, siteCounter, user.getAlgorithmVersion() ) ); assert site != null; if (site == currentSite) { site.setSiteType( siteType ); + site.setAlgorithmVersion( siteVersion ); site.setSiteCounter( siteCounter ); } @@ -208,7 +209,8 @@ public class PasswordFrame extends JFrame implements DocumentListener { @Override public String call() throws Exception { - return user.getKey().encode( site.getSiteName(), site.getSiteType(), site.getSiteCounter(), MPSiteVariant.Password, null ); + return user.getKey( site.getAlgorithmVersion() ) + .encode( site.getSiteName(), site.getSiteType(), site.getSiteCounter(), MPSiteVariant.Password, null ); } } ); Futures.addCallback( passwordFuture, new FutureCallback() { @@ -221,6 +223,7 @@ public class PasswordFrame extends JFrame implements DocumentListener { currentSite = site; siteAddButton.setVisible( user instanceof ModelUser && !(currentSite instanceof ModelSite) ); siteTypeField.setSelectedItem( currentSite.getSiteType() ); + siteVersionField.setSelectedItem( currentSite.getAlgorithmVersion() ); siteCounterField.setValue( currentSite.getSiteCounter() ); siteNameField.setText( currentSite.getSiteName() ); if (siteNameField.getText().startsWith( siteNameQuery )) diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/Site.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/Site.java index 9e56788e..9cee01e2 100644 --- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/Site.java +++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/Site.java @@ -3,6 +3,7 @@ package com.lyndir.masterpassword.gui; import static com.lyndir.lhunath.opal.system.util.StringUtils.strf; import com.lyndir.masterpassword.MPSiteType; +import com.lyndir.masterpassword.MasterKey; /** @@ -18,6 +19,10 @@ public abstract class Site { public abstract void setSiteType(final MPSiteType siteType); + public abstract MasterKey.Version getAlgorithmVersion(); + + public abstract void setAlgorithmVersion(final MasterKey.Version algorithmVersion); + public abstract int getSiteCounter(); public abstract void setSiteCounter(final int siteCounter); diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/UnlockFrame.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/UnlockFrame.java index 7d74eb37..779e6c1e 100644 --- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/UnlockFrame.java +++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/UnlockFrame.java @@ -6,7 +6,6 @@ import com.lyndir.masterpassword.util.Components; import java.awt.*; import java.awt.event.*; import javax.swing.*; -import javax.swing.border.*; /** @@ -14,13 +13,13 @@ import javax.swing.border.*; */ public class UnlockFrame extends JFrame { - private final SignInCallback signInCallback; - private final JPanel root; - private final JButton signInButton; - private final JPanel authenticationContainer; - private AuthenticationPanel authenticationPanel; - private boolean incognito; - public User user; + private final SignInCallback signInCallback; + private final Components.GradientPanel root; + private final JButton signInButton; + private final JPanel authenticationContainer; + private AuthenticationPanel authenticationPanel; + private boolean incognito; + public User user; public UnlockFrame(final SignInCallback signInCallback) throws HeadlessException { @@ -28,11 +27,11 @@ public class UnlockFrame extends JFrame { this.signInCallback = signInCallback; setDefaultCloseOperation( DISPOSE_ON_CLOSE ); - setContentPane( root = new JPanel( new BorderLayout( 20, 20 ) ) ); - root.setBackground( Res.colors().frameBg() ); - root.setBorder( new EmptyBorder( 20, 20, 20, 20 ) ); + setContentPane( root = Components.gradientPanel( new BorderLayout( 20, 20 ), Res.colors().frameBg() ) ); + root.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) ); authenticationContainer = Components.boxLayout( BoxLayout.PAGE_AXIS ); + authenticationContainer.setOpaque( true ); authenticationContainer.setBackground( Res.colors().controlBg() ); authenticationContainer.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) ); add( Components.bordered( authenticationContainer, BorderFactory.createRaisedBevelBorder(), Res.colors().frameBg() ) ); @@ -42,7 +41,6 @@ public class UnlockFrame extends JFrame { Box.createGlue() ); signInBox.setBackground( null ); root.add( signInBox, BorderLayout.SOUTH ); - signInButton.setAlignmentX( LEFT_ALIGNMENT ); signInButton.addActionListener( new AbstractAction() { @Override public void actionPerformed(final ActionEvent e) { @@ -75,7 +73,6 @@ public class UnlockFrame extends JFrame { authenticationContainer.add( Components.stud() ); final JCheckBox incognitoCheckBox = Components.checkBox( "Incognito" ); - incognitoCheckBox.setAlignmentX( LEFT_ALIGNMENT ); incognitoCheckBox.setSelected( incognito ); incognitoCheckBox.addItemListener( new ItemListener() { @Override @@ -91,7 +88,6 @@ public class UnlockFrame extends JFrame { } ); JComponent toolsPanel = Components.boxLayout( BoxLayout.LINE_AXIS, incognitoCheckBox, Box.createGlue() ); - toolsPanel.setAlignmentX( Component.LEFT_ALIGNMENT ); authenticationContainer.add( toolsPanel ); for (JButton button : authenticationPanel.getButtons()) { button.setMargin( new Insets( 0, 0, 0, 0 ) ); @@ -118,7 +114,7 @@ public class UnlockFrame extends JFrame { } boolean checkSignIn() { - boolean enabled = user != null && !user.getFullName().isEmpty() && user.hasKey(); + boolean enabled = user != null && !user.getFullName().isEmpty() && user.isKeyAvailable(); signInButton.setEnabled( enabled ); return enabled; diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/User.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/User.java index eff50565..784257ba 100644 --- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/User.java +++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/User.java @@ -2,9 +2,9 @@ package com.lyndir.masterpassword.gui; import static com.lyndir.lhunath.opal.system.util.StringUtils.*; +import com.google.common.collect.Maps; import com.lyndir.masterpassword.MasterKey; -import com.lyndir.masterpassword.model.MPUser; -import java.security.KeyException; +import java.util.EnumMap; import java.util.Objects; import javax.annotation.Nonnull; @@ -14,7 +14,8 @@ import javax.annotation.Nonnull; */ public abstract class User { - private MasterKey key; + @Nonnull + private static final EnumMap keyByVersion = Maps.newEnumMap( MasterKey.Version.class ); public abstract String getFullName(); @@ -22,32 +23,42 @@ public abstract class User { public abstract MasterKey.Version getAlgorithmVersion(); + public abstract void setAlgorithmVersion(final MasterKey.Version algorithmVersion); + public int getAvatar() { return 0; } - public boolean hasKey() { + public boolean isKeyAvailable() { String masterPassword = getMasterPassword(); - return key != null || (masterPassword != null && !masterPassword.isEmpty()); + return masterPassword != null && !masterPassword.isEmpty(); } @Nonnull public MasterKey getKey() throws MasterKeyException { - if (key == null) { - String masterPassword = getMasterPassword(); - if (masterPassword == null || masterPassword.isEmpty()) { - reset(); - throw new MasterKeyException( strf( "Master password unknown for user: %s", getFullName() ) ); - } + return getKey( getAlgorithmVersion() ); + } - key = MasterKey.create( getAlgorithmVersion(), getFullName(), masterPassword ); + @Nonnull + public MasterKey getKey(MasterKey.Version algorithmVersion) throws MasterKeyException { + String masterPassword = getMasterPassword(); + if (masterPassword == null || masterPassword.isEmpty()) { + reset(); + throw new MasterKeyException( strf( "Master password unknown for user: %s", getFullName() ) ); } + MasterKey key = keyByVersion.get( algorithmVersion ); + if (key == null) + keyByVersion.put( algorithmVersion, key = MasterKey.create( algorithmVersion, getFullName(), masterPassword ) ); + if (!key.isValid()) + key.revalidate( masterPassword ); + return key; } public void reset() { - key = null; + for (MasterKey key : keyByVersion.values()) + key.invalidate(); } public abstract Iterable findSitesByName(final String siteName); diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/util/Components.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/util/Components.java index 522db2a8..617a769c 100644 --- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/util/Components.java +++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/util/Components.java @@ -1,5 +1,6 @@ package com.lyndir.masterpassword.util; +import com.lyndir.masterpassword.gui.ModelUser; import com.lyndir.masterpassword.gui.Res; import java.awt.*; import javax.swing.*; @@ -12,8 +13,9 @@ import javax.swing.border.CompoundBorder; */ public abstract class Components { - public static JPanel boxLayout(int axis, Component... components) { - JPanel container = new JPanel(); + public static GradientPanel boxLayout(int axis, Component... components) { + GradientPanel container = gradientPanel( null, null ); + // container.setBackground( Color.red ); container.setLayout( new BoxLayout( container, axis ) ); for (Component component : components) container.add( component ); @@ -21,12 +23,12 @@ public abstract class Components { return container; } - public static JPanel bordered(final JComponent component, final Border border) { + public static GradientPanel bordered(final JComponent component, final Border border) { return bordered( component, border, null ); } - public static JPanel bordered(final JComponent component, final Border border, Color background) { - JPanel box = boxLayout( BoxLayout.LINE_AXIS, component ); + public static GradientPanel bordered(final JComponent component, final Border border, Color background) { + GradientPanel box = boxLayout( BoxLayout.LINE_AXIS, component ); if (border != null) box.setBorder( border ); @@ -37,12 +39,25 @@ public abstract class Components { return box; } + public static GradientPanel gradientPanel(final LayoutManager layout, final Color color) { + return new GradientPanel( layout, color ) { + { + setOpaque( color != null ); + setBackground( null ); + setAlignmentX( LEFT_ALIGNMENT ); + setAlignmentY( BOTTOM_ALIGNMENT ); + } + }; + } + public static JTextField textField() { return new JTextField() { { setBorder( BorderFactory.createCompoundBorder( BorderFactory.createLineBorder( Res.colors().controlBorder(), 1, true ), BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) ) ); setFont( Res.valueFont().deriveFont( 12f ) ); + setAlignmentX( LEFT_ALIGNMENT ); + setAlignmentY( BOTTOM_ALIGNMENT ); } @Override @@ -57,6 +72,8 @@ public abstract class Components { { setBorder( BorderFactory.createCompoundBorder( BorderFactory.createLineBorder( Res.colors().controlBorder(), 1, true ), BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) ) ); + setAlignmentX( LEFT_ALIGNMENT ); + setAlignmentY( BOTTOM_ALIGNMENT ); } @Override @@ -70,6 +87,8 @@ public abstract class Components { return new JButton( label ) { { setFont( Res.controlFont().deriveFont( 12f ) ); + setAlignmentX( LEFT_ALIGNMENT ); + setAlignmentY( BOTTOM_ALIGNMENT ); } @Override @@ -80,7 +99,12 @@ public abstract class Components { } public static Component stud() { - return Box.createRigidArea( new Dimension( 8, 8 ) ); + Dimension studDimension = new Dimension( 8, 8 ); + Box.Filler rigidArea = new Box.Filler( studDimension, studDimension, studDimension ); + rigidArea.setAlignmentX( Component.LEFT_ALIGNMENT ); + rigidArea.setAlignmentY( Component.BOTTOM_ALIGNMENT ); + rigidArea.setBackground( Color.red ); + return rigidArea; } public static JSpinner spinner(final SpinnerModel model) { @@ -90,6 +114,8 @@ public abstract class Components { BorderFactory.createLineBorder( Res.colors().controlBorder(), 1, true ), BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) ); ((DefaultEditor) getEditor()).getTextField().setBorder( editorBorder ); + setAlignmentX( LEFT_ALIGNMENT ); + setAlignmentY( BOTTOM_ALIGNMENT ); } @Override @@ -107,6 +133,13 @@ public abstract class Components { return new JLabel( label, alignment ) { { setFont( Res.controlFont().deriveFont( 12f ) ); + setAlignmentX( LEFT_ALIGNMENT ); + setAlignmentY( BOTTOM_ALIGNMENT ); + } + + @Override + public Dimension getMaximumSize() { + return new Dimension( Integer.MAX_VALUE, getPreferredSize().height ); } }; } @@ -115,7 +148,67 @@ public abstract class Components { return new JCheckBox( label ) { { setFont( Res.controlFont().deriveFont( 12f ) ); + setAlignmentX( LEFT_ALIGNMENT ); + setAlignmentY( BOTTOM_ALIGNMENT ); } }; } + + @SafeVarargs + public static JComboBox comboBox(final V... values) { + return comboBox( new DefaultComboBoxModel<>( values ) ); + } + + public static JComboBox comboBox(final ComboBoxModel model) { + return new JComboBox( model ) { + { + setFont( Res.controlFont().deriveFont( 12f ) ); + setAlignmentX( LEFT_ALIGNMENT ); + setAlignmentY( BOTTOM_ALIGNMENT ); + } + + @Override + public Dimension getMaximumSize() { + return new Dimension( Integer.MAX_VALUE, getPreferredSize().height ); + } + }; + } + + public static class GradientPanel extends JPanel { + + private Color gradientColor; + private GradientPaint paint; + + protected GradientPanel(final LayoutManager layout, final Color gradientColor) { + super( layout ); + this.gradientColor = gradientColor; + setBackground( null ); + } + + public Color getGradientColor() { + return gradientColor; + } + + public void setGradientColor(final Color gradientColor) { + this.gradientColor = gradientColor; + } + + @Override + public void doLayout() { + super.doLayout(); + + if (gradientColor != null) + paint = new GradientPaint( new Point( 0, 0 ), gradientColor, new Point( getWidth(), getHeight() ), gradientColor.darker() ); + } + + @Override + protected void paintComponent(final Graphics g) { + super.paintComponent( g ); + + if (paint != null) { + ((Graphics2D) g).setPaint( paint ); + g.fillRect( 0, 0, getWidth(), getHeight() ); + } + } + } } diff --git a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSite.java b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSite.java index 6c578341..e08ec8ac 100644 --- a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSite.java +++ b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSite.java @@ -6,7 +6,6 @@ import static com.lyndir.lhunath.opal.system.util.StringUtils.strf; import com.lyndir.masterpassword.*; import java.util.Objects; import javax.annotation.Nullable; -import org.joda.time.DateTime; import org.joda.time.Instant; @@ -19,7 +18,7 @@ public class MPSite { public static final int DEFAULT_COUNTER = 1; private final MPUser user; - private MasterKey.Version mpVersion; + private MasterKey.Version algorithmVersion; private Instant lastUsed; private String siteName; private MPSiteType siteType; @@ -33,17 +32,17 @@ public class MPSite { public MPSite(final MPUser user, final String siteName, final MPSiteType siteType, final int siteCounter) { this.user = user; - this.mpVersion = MasterKey.Version.CURRENT; + this.algorithmVersion = MasterKey.Version.CURRENT; this.lastUsed = new Instant(); this.siteName = siteName; this.siteType = siteType; this.siteCounter = siteCounter; } - protected MPSite(final MPUser user, final MasterKey.Version mpVersion, final Instant lastUsed, final String siteName, final MPSiteType siteType, final int siteCounter, + protected MPSite(final MPUser user, final MasterKey.Version algorithmVersion, final Instant lastUsed, final String siteName, final MPSiteType siteType, final int siteCounter, final int uses, final String loginName, final String importContent) { this.user = user; - this.mpVersion = mpVersion; + this.algorithmVersion = algorithmVersion; this.lastUsed = lastUsed; this.siteName = siteName; this.siteType = siteType; @@ -69,12 +68,12 @@ public class MPSite { return null; } - public MasterKey.Version getMPVersion() { - return mpVersion; + public MasterKey.Version getAlgorithmVersion() { + return algorithmVersion; } - public void setMPVersion(final MasterKey.Version mpVersion) { - this.mpVersion = mpVersion; + public void setAlgorithmVersion(final MasterKey.Version mpVersion) { + this.algorithmVersion = mpVersion; } public Instant getLastUsed() { diff --git a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSiteMarshaller.java b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSiteMarshaller.java index 6a20757c..374bd8cd 100644 --- a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSiteMarshaller.java +++ b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSiteMarshaller.java @@ -82,7 +82,7 @@ public class MPSiteMarshaller { site.getUses(), // uses strf( "%d:%d:%d", // site.getSiteType().getType(), // type - site.getMPVersion(), // algorithm + site.getAlgorithmVersion(), // algorithm site.getSiteCounter() ), // counter ifNotNullElse( site.getLoginName(), "" ), // loginName site.getSiteName(), // siteName