Moar UI work on the Java app + support for per-site algorithm versioning.
This commit is contained in:
parent
78c593fc08
commit
a6ab9b9194
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
|
@ -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 ) );
|
||||
|
@ -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();
|
||||
|
@ -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 );
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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<ModelUser>( 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 );
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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<MPSiteType> 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<MPSiteType> siteTypeField;
|
||||
private final JComboBox<MasterKey.Version> 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<String>() {
|
||||
@ -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 ))
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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<MasterKey.Version, MasterKey> 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<Site> findSitesByName(final String siteName);
|
||||
|
@ -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 <V> JComboBox<V> comboBox(final V... values) {
|
||||
return comboBox( new DefaultComboBoxModel<>( values ) );
|
||||
}
|
||||
|
||||
public static <M> JComboBox<M> comboBox(final ComboBoxModel<M> model) {
|
||||
return new JComboBox<M>( 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() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user