2
0

Full ability to load, add and autocomplete sites from history.

This commit is contained in:
Maarten Billemont 2014-12-16 22:13:11 -05:00
parent 97dcc65eac
commit c2a6a3d035
16 changed files with 338 additions and 81 deletions

View File

@ -21,6 +21,7 @@ public class IncognitoAuthenticationPanel extends AuthenticationPanel implements
// Full Name // Full Name
super( unlockFrame ); super( unlockFrame );
JLabel fullNameLabel = new JLabel( "Full Name:" ); JLabel fullNameLabel = new JLabel( "Full Name:" );
fullNameLabel.setFont( Res.exoRegular().deriveFont( 12f ) );
fullNameLabel.setAlignmentX( LEFT_ALIGNMENT ); fullNameLabel.setAlignmentX( LEFT_ALIGNMENT );
fullNameLabel.setHorizontalAlignment( SwingConstants.CENTER ); fullNameLabel.setHorizontalAlignment( SwingConstants.CENTER );
fullNameLabel.setVerticalAlignment( SwingConstants.BOTTOM ); fullNameLabel.setVerticalAlignment( SwingConstants.BOTTOM );
@ -32,6 +33,7 @@ public class IncognitoAuthenticationPanel extends AuthenticationPanel implements
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height ); return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
} }
}; };
fullNameField.setFont( Res.sourceCodeProRegular().deriveFont( 12f ) );
fullNameField.setAlignmentX( LEFT_ALIGNMENT ); fullNameField.setAlignmentX( LEFT_ALIGNMENT );
fullNameField.getDocument().addDocumentListener( this ); fullNameField.getDocument().addDocumentListener( this );
fullNameField.addActionListener( this ); fullNameField.addActionListener( this );
@ -39,6 +41,7 @@ public class IncognitoAuthenticationPanel extends AuthenticationPanel implements
// Master Password // Master Password
JLabel masterPasswordLabel = new JLabel( "Master Password:" ); JLabel masterPasswordLabel = new JLabel( "Master Password:" );
masterPasswordLabel.setFont( Res.exoRegular().deriveFont( 12f ) );
masterPasswordLabel.setAlignmentX( LEFT_ALIGNMENT ); masterPasswordLabel.setAlignmentX( LEFT_ALIGNMENT );
masterPasswordLabel.setHorizontalAlignment( SwingConstants.CENTER ); masterPasswordLabel.setHorizontalAlignment( SwingConstants.CENTER );
masterPasswordLabel.setVerticalAlignment( SwingConstants.BOTTOM ); masterPasswordLabel.setVerticalAlignment( SwingConstants.BOTTOM );

View File

@ -0,0 +1,44 @@
package com.lyndir.masterpassword.gui;
import com.lyndir.masterpassword.MPSiteType;
/**
* @author lhunath, 14-12-16
*/
public class IncognitoSite extends Site {
private String siteName;
private MPSiteType siteType;
private int siteCounter;
public IncognitoSite(final String siteName, final MPSiteType siteType, final int siteCounter) {
this.siteName = siteName;
this.siteType = siteType;
this.siteCounter = siteCounter;
}
public String getSiteName() {
return siteName;
}
public void setSiteName(final String siteName) {
this.siteName = siteName;
}
public MPSiteType getSiteType() {
return siteType;
}
public void setSiteType(final MPSiteType siteType) {
this.siteType = siteType;
}
public int getSiteCounter() {
return siteCounter;
}
public void setSiteCounter(final int siteCounter) {
this.siteCounter = siteCounter;
}
}

View File

@ -1,5 +1,8 @@
package com.lyndir.masterpassword.gui; package com.lyndir.masterpassword.gui;
import com.google.common.collect.ImmutableList;
/** /**
* @author lhunath, 2014-06-08 * @author lhunath, 2014-06-08
*/ */
@ -21,4 +24,13 @@ public class IncognitoUser extends User {
protected String getMasterPassword() { protected String getMasterPassword() {
return masterPassword; return masterPassword;
} }
@Override
public Iterable<Site> findSitesByName(final String siteName) {
return ImmutableList.of();
}
@Override
public void addSite(final Site site) {
}
} }

View File

@ -2,6 +2,7 @@ package com.lyndir.masterpassword.gui;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.collect.*; import com.google.common.collect.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.model.MPUser; import com.lyndir.masterpassword.model.MPUser;
import com.lyndir.masterpassword.model.MPUserFileManager; import com.lyndir.masterpassword.model.MPUserFileManager;
import java.awt.*; import java.awt.*;
@ -17,6 +18,9 @@ import javax.swing.event.DocumentListener;
*/ */
public class ModelAuthenticationPanel extends AuthenticationPanel implements ItemListener, ActionListener, DocumentListener { public class ModelAuthenticationPanel extends AuthenticationPanel implements ItemListener, ActionListener, DocumentListener {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( ModelAuthenticationPanel.class );
private final JComboBox<ModelUser> userField; private final JComboBox<ModelUser> userField;
private final JLabel masterPasswordLabel; private final JLabel masterPasswordLabel;
private final JPasswordField masterPasswordField; private final JPasswordField masterPasswordField;
@ -38,6 +42,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
// User // User
JLabel userLabel = new JLabel( "User:" ); JLabel userLabel = new JLabel( "User:" );
userLabel.setFont( Res.exoRegular().deriveFont( 12f ) );
userLabel.setAlignmentX( LEFT_ALIGNMENT ); userLabel.setAlignmentX( LEFT_ALIGNMENT );
userLabel.setHorizontalAlignment( SwingConstants.CENTER ); userLabel.setHorizontalAlignment( SwingConstants.CENTER );
userLabel.setVerticalAlignment( SwingConstants.BOTTOM ); userLabel.setVerticalAlignment( SwingConstants.BOTTOM );
@ -49,6 +54,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height ); return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
} }
}; };
userField.setFont( Res.sourceCodeProRegular().deriveFont( 12f ) );
userField.setAlignmentX( LEFT_ALIGNMENT ); userField.setAlignmentX( LEFT_ALIGNMENT );
userField.addItemListener( this ); userField.addItemListener( this );
userField.addActionListener( this ); userField.addActionListener( this );
@ -56,6 +62,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
// Master Password // Master Password
masterPasswordLabel = new JLabel( "Master Password:" ); masterPasswordLabel = new JLabel( "Master Password:" );
masterPasswordLabel.setFont( Res.exoRegular().deriveFont( 12f ) );
masterPasswordLabel.setAlignmentX( LEFT_ALIGNMENT ); masterPasswordLabel.setAlignmentX( LEFT_ALIGNMENT );
masterPasswordLabel.setHorizontalAlignment( SwingConstants.CENTER ); masterPasswordLabel.setHorizontalAlignment( SwingConstants.CENTER );
masterPasswordLabel.setVerticalAlignment( SwingConstants.BOTTOM ); masterPasswordLabel.setVerticalAlignment( SwingConstants.BOTTOM );

View File

@ -0,0 +1,51 @@
package com.lyndir.masterpassword.gui;
import com.lyndir.masterpassword.MPSiteType;
import com.lyndir.masterpassword.model.*;
/**
* @author lhunath, 14-12-16
*/
public class ModelSite extends Site {
private final MPSite model;
public ModelSite(final MPSiteResult result) {
this.model = result.getSite();
}
public String getSiteName() {
return model.getSiteName();
}
@Override
public void setSiteName(final String siteName) {
model.setSiteName( siteName );
MPUserFileManager.get().save();
}
public MPSiteType getSiteType() {
return model.getSiteType();
}
@Override
public void setSiteType(final MPSiteType siteType) {
if (siteType != getSiteType()) {
model.setSiteType( siteType );
MPUserFileManager.get().save();
}
}
public int getSiteCounter() {
return model.getSiteCounter();
}
@Override
public void setSiteCounter(final int siteCounter) {
if (siteCounter != getSiteCounter()) {
model.setSiteCounter( siteCounter );
MPUserFileManager.get().save();
}
}
}

View File

@ -2,9 +2,12 @@ package com.lyndir.masterpassword.gui;
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf; import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
import com.google.common.base.Function;
import com.google.common.collect.FluentIterable;
import com.lyndir.masterpassword.MasterKey; import com.lyndir.masterpassword.MasterKey;
import com.lyndir.masterpassword.model.MPUser; import com.lyndir.masterpassword.model.*;
import com.lyndir.masterpassword.model.MPUserFileManager; import javax.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
/** /**
@ -12,16 +15,20 @@ import com.lyndir.masterpassword.model.MPUserFileManager;
*/ */
public class ModelUser extends User { public class ModelUser extends User {
private final MPUser user; private final MPUser model;
private String masterPassword; private String masterPassword;
public ModelUser(MPUser user) { public ModelUser(MPUser model) {
this.user = user; this.model = model;
}
public MPUser getModel() {
return model;
} }
@Override @Override
public String getFullName() { public String getFullName() {
return user.getFullName(); return model.getFullName();
} }
@Override @Override
@ -31,11 +38,11 @@ public class ModelUser extends User {
@Override @Override
public int getAvatar() { public int getAvatar() {
return user.getAvatar(); return model.getAvatar();
} }
public void setAvatar(final int avatar) { public void setAvatar(final int avatar) {
user.setAvatar( avatar % Res.avatars() ); model.setAvatar( avatar % Res.avatars() );
MPUserFileManager.get().save(); MPUserFileManager.get().save();
} }
@ -43,18 +50,36 @@ public class ModelUser extends User {
this.masterPassword = masterPassword; this.masterPassword = masterPassword;
} }
@NotNull
@Override @Override
public MasterKey getKey() { public MasterKey getKey() {
MasterKey key = super.getKey(); MasterKey key = super.getKey();
if (!user.hasKeyID()) { if (!model.hasKeyID()) {
user.setKeyID( key.getKeyID() ); model.setKeyID( key.getKeyID() );
MPUserFileManager.get().save(); MPUserFileManager.get().save();
} else if (!user.hasKeyID( key.getKeyID() )) } else if (!model.hasKeyID( key.getKeyID() ))
throw new IllegalStateException( strf( "Incorrect master password for user: %s", getFullName() ) ); throw new IllegalStateException( strf( "Incorrect master password for user: %s", getFullName() ) );
return key; return key;
} }
@Override
public Iterable<Site> findSitesByName(final String query) {
return FluentIterable.from( model.findSitesByName( query ) ).transform( new Function<MPSiteResult, Site>() {
@Nullable
@Override
public Site apply(final MPSiteResult result) {
return new ModelSite( result );
}
} );
}
@Override
public void addSite(final Site site) {
model.addSite( new MPSite( site.getSiteName(), site.getSiteType(), site.getSiteCounter() ) );
MPUserFileManager.get().save();
}
public boolean keySaved() { public boolean keySaved() {
return false; return false;
} }

View File

@ -3,11 +3,15 @@ package com.lyndir.masterpassword.gui;
import static com.lyndir.lhunath.opal.system.util.StringUtils.*; import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.*; import com.lyndir.masterpassword.*;
import com.lyndir.masterpassword.util.Components; import com.lyndir.masterpassword.util.Components;
import java.awt.*; import java.awt.*;
import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.StringSelection;
import java.awt.event.*; import java.awt.event.*;
import java.util.concurrent.Callable;
import javax.annotation.Nonnull;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.*; import javax.swing.border.*;
import javax.swing.event.*; import javax.swing.event.*;
@ -20,10 +24,13 @@ public class PasswordFrame extends JFrame implements DocumentListener {
private final User user; private final User user;
private final JTextField siteNameField; private final JTextField siteNameField;
private final JButton siteAddButton;
private final JComboBox<MPSiteType> siteTypeField; private final JComboBox<MPSiteType> siteTypeField;
private final JSpinner siteCounterField; private final JSpinner siteCounterField;
private final JTextField passwordField; private final JTextField passwordField;
private final JLabel tipLabel; private final JLabel tipLabel;
private boolean updatingUI;
private Site currentSite;
public PasswordFrame(User user) public PasswordFrame(User user)
throws HeadlessException { throws HeadlessException {
@ -54,21 +61,40 @@ public class PasswordFrame extends JFrame implements DocumentListener {
label.setFont( Res.exoRegular().deriveFont( 12f ) ); label.setFont( Res.exoRegular().deriveFont( 12f ) );
label.setAlignmentX( LEFT_ALIGNMENT ); label.setAlignmentX( LEFT_ALIGNMENT );
sitePanel.add( siteNameField = new JTextField() { JComponent siteControls = Components.boxLayout( BoxLayout.LINE_AXIS, //
siteNameField = new JTextField() {
@Override
public Dimension getMaximumSize() {
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
}
}, siteAddButton = new JButton( "Add Site" ) {
@Override
public Dimension getMaximumSize() {
return new Dimension( 20, getPreferredSize().height );
}
} );
siteAddButton.setVisible( false );
siteAddButton.setFont( Res.exoRegular().deriveFont( 12f ) );
siteAddButton.setAlignmentX( RIGHT_ALIGNMENT );
siteAddButton.setAlignmentY( CENTER_ALIGNMENT );
siteAddButton.addActionListener( new ActionListener() {
@Override @Override
public Dimension getMaximumSize() { public void actionPerformed(final ActionEvent e) {
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height ); PasswordFrame.this.user.addSite( currentSite );
siteAddButton.setVisible( false );
} }
} ); } );
siteNameField.setFont( Res.exoRegular().deriveFont( 12f ) ); siteControls.setAlignmentX( LEFT_ALIGNMENT );
sitePanel.add( siteControls );
siteNameField.setFont( Res.sourceCodeProRegular().deriveFont( 12f ) );
siteNameField.setAlignmentX( LEFT_ALIGNMENT ); siteNameField.setAlignmentX( LEFT_ALIGNMENT );
siteNameField.getDocument().addDocumentListener( this ); siteNameField.getDocument().addDocumentListener( this );
siteNameField.addActionListener( new ActionListener() { siteNameField.addActionListener( new ActionListener() {
@Override @Override
public void actionPerformed(final ActionEvent e) { public void actionPerformed(final ActionEvent e) {
updatePassword( new PasswordCallback() { Futures.addCallback( updatePassword(), new FutureCallback<String>() {
@Override @Override
public void passwordGenerated(final String siteName, final String sitePassword) { public void onSuccess(final String sitePassword) {
StringSelection clipboardContents = new StringSelection( sitePassword ); StringSelection clipboardContents = new StringSelection( sitePassword );
Toolkit.getDefaultToolkit().getSystemClipboard().setContents( clipboardContents, null ); Toolkit.getDefaultToolkit().getSystemClipboard().setContents( clipboardContents, null );
@ -85,6 +111,10 @@ public class PasswordFrame extends JFrame implements DocumentListener {
} }
} ); } );
} }
@Override
public void onFailure(final Throwable t) {
}
} ); } );
} }
} ); } );
@ -102,24 +132,24 @@ public class PasswordFrame extends JFrame implements DocumentListener {
} ); } );
siteSettings.setAlignmentX( LEFT_ALIGNMENT ); siteSettings.setAlignmentX( LEFT_ALIGNMENT );
sitePanel.add( siteSettings ); sitePanel.add( siteSettings );
siteTypeField.setFont( Res.exoRegular().deriveFont( 12f ) ); siteTypeField.setFont( Res.sourceCodeProRegular().deriveFont( 12f ) );
siteTypeField.setAlignmentX( LEFT_ALIGNMENT ); siteTypeField.setAlignmentX( LEFT_ALIGNMENT );
siteTypeField.setAlignmentY( CENTER_ALIGNMENT ); siteTypeField.setAlignmentY( CENTER_ALIGNMENT );
siteTypeField.setSelectedItem( MPSiteType.GeneratedLong ); siteTypeField.setSelectedItem( MPSiteType.GeneratedLong );
siteTypeField.addItemListener( new ItemListener() { siteTypeField.addItemListener( new ItemListener() {
@Override @Override
public void itemStateChanged(final ItemEvent e) { public void itemStateChanged(final ItemEvent e) {
updatePassword( null ); updatePassword();
} }
} ); } );
siteCounterField.setFont( Res.exoRegular().deriveFont( 12f ) ); siteCounterField.setFont( Res.sourceCodeProRegular().deriveFont( 12f ) );
siteCounterField.setAlignmentX( RIGHT_ALIGNMENT ); siteCounterField.setAlignmentX( RIGHT_ALIGNMENT );
siteCounterField.setAlignmentY( CENTER_ALIGNMENT ); siteCounterField.setAlignmentY( CENTER_ALIGNMENT );
siteCounterField.addChangeListener( new ChangeListener() { siteCounterField.addChangeListener( new ChangeListener() {
@Override @Override
public void stateChanged(final ChangeEvent e) { public void stateChanged(final ChangeEvent e) {
updatePassword( null ); updatePassword();
} }
} ); } );
@ -132,7 +162,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
// Tip // Tip
tipLabel = new JLabel( " ", JLabel.CENTER ); tipLabel = new JLabel( " ", JLabel.CENTER );
tipLabel.setFont( Res.exoThin().deriveFont( 9f ) ); tipLabel.setFont( Res.exoRegular().deriveFont( 9f ) );
tipLabel.setAlignmentX( Component.CENTER_ALIGNMENT ); tipLabel.setAlignmentX( Component.CENTER_ALIGNMENT );
add( Components.boxLayout( BoxLayout.PAGE_AXIS, passwordField, tipLabel ), BorderLayout.SOUTH ); add( Components.boxLayout( BoxLayout.PAGE_AXIS, passwordField, tipLabel ), BorderLayout.SOUTH );
@ -146,55 +176,76 @@ public class PasswordFrame extends JFrame implements DocumentListener {
setLocationRelativeTo( null ); setLocationRelativeTo( null );
} }
private void updatePassword(final PasswordCallback callback) { @Nonnull
final MPSiteType siteType = (MPSiteType) siteTypeField.getSelectedItem(); private ListenableFuture<String> updatePassword() {
final String siteName = siteNameField.getText();
final int siteCounter = (Integer) siteCounterField.getValue();
if (siteType.getTypeClass() != MPSiteTypeClass.Generated || siteName == null || siteName.isEmpty() || !user.hasKey()) { final String siteNameQuery = siteNameField.getText();
if (updatingUI)
return Futures.immediateCancelledFuture();
if (siteNameQuery == null || siteNameQuery.isEmpty() || !user.hasKey()) {
passwordField.setText( null ); passwordField.setText( null );
tipLabel.setText( null ); tipLabel.setText( null );
return; return Futures.immediateCancelledFuture();
} }
Res.execute( new Runnable() { MPSiteType siteType = siteTypeField.getModel().getElementAt( siteTypeField.getSelectedIndex() );
@Override final int siteCounter = (Integer) siteCounterField.getValue();
public void run() { final Site site = currentSite != null && currentSite.getSiteName().equals( siteNameQuery )? currentSite
final String sitePassword = user.getKey().encode( siteName, siteType, siteCounter, MPSiteVariant.Password, null ); : Iterables.getFirst( user.findSitesByName( siteNameQuery ), new IncognitoSite( siteNameQuery, siteType, siteCounter ) );
if (callback != null) assert site != null;
callback.passwordGenerated( siteName, sitePassword ); if (site == currentSite) {
site.setSiteType( siteType );
site.setSiteCounter( siteCounter );
}
ListenableFuture<String> passwordFuture = Res.execute( new Callable<String>() {
@Override
public String call()
throws Exception {
return user.getKey().encode( site.getSiteName(), site.getSiteType(), site.getSiteCounter(), MPSiteVariant.Password, null );
}
} );
Futures.addCallback( passwordFuture, new FutureCallback<String>() {
@Override
public void onSuccess(final String sitePassword) {
SwingUtilities.invokeLater( new Runnable() { SwingUtilities.invokeLater( new Runnable() {
@Override @Override
public void run() { public void run() {
if (!siteName.equals( siteNameField.getText() )) updatingUI = true;
return; currentSite = site;
siteAddButton.setVisible( user instanceof ModelUser && !(currentSite instanceof ModelSite) );
siteTypeField.setSelectedItem( currentSite.getSiteType() );
siteCounterField.setValue( currentSite.getSiteCounter() );
siteNameField.setText( currentSite.getSiteName() );
if (siteNameField.getText().startsWith( siteNameQuery ))
siteNameField.select( siteNameQuery.length(), siteNameField.getText().length() );
passwordField.setText( sitePassword ); passwordField.setText( sitePassword );
tipLabel.setText( "Press [Enter] to copy the password." ); tipLabel.setText( "Press [Enter] to copy the password. Then paste it into the password field." );
updatingUI = false;
} }
} ); } );
} }
@Override
public void onFailure(final Throwable t) {
}
} ); } );
return passwordFuture;
} }
@Override @Override
public void insertUpdate(final DocumentEvent e) { public void insertUpdate(final DocumentEvent e) {
updatePassword( null ); updatePassword();
} }
@Override @Override
public void removeUpdate(final DocumentEvent e) { public void removeUpdate(final DocumentEvent e) {
updatePassword( null );
} }
@Override @Override
public void changedUpdate(final DocumentEvent e) { public void changedUpdate(final DocumentEvent e) {
updatePassword( null ); updatePassword();
}
interface PasswordCallback {
void passwordGenerated(String siteName, String sitePassword);
} }
} }

View File

@ -5,13 +5,13 @@ import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
import com.google.common.base.Throwables; import com.google.common.base.Throwables;
import com.google.common.io.Resources; import com.google.common.io.Resources;
import com.google.common.util.concurrent.*;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import java.awt.*; import java.awt.*;
import java.awt.image.ImageObserver; import java.awt.image.ImageObserver;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.util.concurrent.ExecutorService; import java.util.concurrent.*;
import java.util.concurrent.Executors;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.swing.*; import javax.swing.*;
@ -25,14 +25,15 @@ public abstract class Res {
private static final ExecutorService executor = Executors.newSingleThreadExecutor(); private static final ExecutorService executor = Executors.newSingleThreadExecutor();
private static final Logger logger = Logger.get( Res.class ); private static final Logger logger = Logger.get( Res.class );
private static Font sourceCodeProRegular;
private static Font sourceCodeProBlack; private static Font sourceCodeProBlack;
private static Font exoBold; private static Font exoBold;
private static Font exoExtraBold; private static Font exoExtraBold;
private static Font exoRegular; private static Font exoRegular;
private static Font exoThin; private static Font exoThin;
public static void execute(final Runnable job) { public static Future<?> execute(final Runnable job) {
executor.submit( new Runnable() { return executor.submit( new Runnable() {
@Override @Override
public void run() { public void run() {
try { try {
@ -45,6 +46,22 @@ public abstract class Res {
} ); } );
} }
public static <V> ListenableFuture<V> execute(final Callable<V> job) {
return JdkFutureAdapters.listenInPoolThread( executor.submit( new Callable<V>() {
@Override
public V call()
throws Exception {
try {
return job.call();
}
catch (Throwable t) {
logger.err( t, "Unexpected: %s", t.getLocalizedMessage() );
throw t;
}
}
} ), executor );
}
public static Icon iconAdd() { public static Icon iconAdd() {
return new RetinaIcon( Resources.getResource( "media/icon_add@2x.png" ) ); return new RetinaIcon( Resources.getResource( "media/icon_add@2x.png" ) );
} }
@ -61,12 +78,20 @@ public abstract class Res {
return 19; return 19;
} }
public static Font sourceCodeProRegular() {
try {
return sourceCodeProRegular != null? sourceCodeProRegular: (sourceCodeProRegular =
Font.createFont( Font.TRUETYPE_FONT, Resources.getResource( "fonts/SourceCodePro-Regular.otf" ).openStream() ));
}
catch (FontFormatException | IOException e) {
throw Throwables.propagate( e );
}
}
public static Font sourceCodeProBlack() { public static Font sourceCodeProBlack() {
try { try {
URL resource = Resources.getResource( "fonts/SourceCodePro-Bold.otf" ); return sourceCodeProBlack != null? sourceCodeProBlack: (sourceCodeProBlack =
Font font = Font.createFont( Font.TRUETYPE_FONT, resource.openStream() ); Font.createFont( Font.TRUETYPE_FONT, Resources.getResource( "fonts/SourceCodePro-Bold.otf" ).openStream() ));
return sourceCodeProBlack != null? sourceCodeProBlack: //
(sourceCodeProBlack = font);
} }
catch (FontFormatException | IOException e) { catch (FontFormatException | IOException e) {
throw Throwables.propagate( e ); throw Throwables.propagate( e );
@ -75,10 +100,8 @@ public abstract class Res {
public static Font exoBold() { public static Font exoBold() {
try { try {
URL resource = Resources.getResource( "fonts/Exo2.0-Bold.otf" ); return exoBold != null? exoBold: (exoBold =
Font font = Font.createFont( Font.TRUETYPE_FONT, resource.openStream() ); Font.createFont( Font.TRUETYPE_FONT, Resources.getResource( "fonts/Exo2.0-Bold.otf" ).openStream() ));
return exoBold != null? exoBold: //
(exoBold = font);
} }
catch (FontFormatException | IOException e) { catch (FontFormatException | IOException e) {
throw Throwables.propagate( e ); throw Throwables.propagate( e );
@ -87,10 +110,8 @@ public abstract class Res {
public static Font exoExtraBold() { public static Font exoExtraBold() {
try { try {
URL resource = Resources.getResource( "fonts/Exo2.0-ExtraBold.otf" ); return exoExtraBold != null? exoExtraBold: (exoExtraBold
Font font = Font.createFont( Font.TRUETYPE_FONT, resource.openStream() ); = Font.createFont( Font.TRUETYPE_FONT, Resources.getResource( "fonts/Exo2.0-ExtraBold.otf" ).openStream() ));
return exoExtraBold != null? exoExtraBold: //
(exoExtraBold = font);
} }
catch (FontFormatException | IOException e) { catch (FontFormatException | IOException e) {
throw Throwables.propagate( e ); throw Throwables.propagate( e );
@ -99,10 +120,8 @@ public abstract class Res {
public static Font exoRegular() { public static Font exoRegular() {
try { try {
URL resource = Resources.getResource( "fonts/Exo2.0-Regular.otf" ); return exoRegular != null? exoRegular: (exoRegular =
Font font = Font.createFont( Font.TRUETYPE_FONT, resource.openStream() ); Font.createFont( Font.TRUETYPE_FONT, Resources.getResource( "fonts/Exo2.0-Regular.otf" ).openStream() ));
return exoRegular != null? exoRegular: //
(exoRegular = font);
} }
catch (FontFormatException | IOException e) { catch (FontFormatException | IOException e) {
throw Throwables.propagate( e ); throw Throwables.propagate( e );
@ -111,10 +130,8 @@ public abstract class Res {
public static Font exoThin() { public static Font exoThin() {
try { try {
URL resource = Resources.getResource( "fonts/Exo2.0-Thin.otf" ); return exoThin != null? exoThin: (exoThin =
Font font = Font.createFont( Font.TRUETYPE_FONT, resource.openStream() ); Font.createFont( Font.TRUETYPE_FONT, Resources.getResource( "fonts/Exo2.0-Thin.otf" ).openStream() ));
return exoThin != null? exoThin: //
(exoThin = font);
} }
catch (FontFormatException | IOException e) { catch (FontFormatException | IOException e) {
throw Throwables.propagate( e ); throw Throwables.propagate( e );

View File

@ -0,0 +1,29 @@
package com.lyndir.masterpassword.gui;
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
import com.lyndir.masterpassword.MPSiteType;
/**
* @author lhunath, 14-12-16
*/
public abstract class Site {
public abstract String getSiteName();
public abstract void setSiteName(final String siteName);
public abstract MPSiteType getSiteType();
public abstract void setSiteType(final MPSiteType siteType);
public abstract int getSiteCounter();
public abstract void setSiteCounter(final int siteCounter);
@Override
public String toString() {
return strf( "{%s: %s}", getClass().getSimpleName(), getSiteName() );
}
}

View File

@ -38,6 +38,7 @@ public class UnlockFrame extends JFrame {
// Sign In // Sign In
root.add( Components.boxLayout( BoxLayout.LINE_AXIS, Box.createGlue(), signInButton = new JButton( "Sign In" ), Box.createGlue() ), root.add( Components.boxLayout( BoxLayout.LINE_AXIS, Box.createGlue(), signInButton = new JButton( "Sign In" ), Box.createGlue() ),
BorderLayout.SOUTH ); BorderLayout.SOUTH );
signInButton.setFont( Res.exoRegular().deriveFont( 12f ) );
signInButton.setAlignmentX( LEFT_ALIGNMENT ); signInButton.setAlignmentX( LEFT_ALIGNMENT );
signInButton.addActionListener( new AbstractAction() { signInButton.addActionListener( new AbstractAction() {
@Override @Override
@ -73,6 +74,7 @@ public class UnlockFrame extends JFrame {
authenticationContainer.add( authenticationPanel, BorderLayout.CENTER ); authenticationContainer.add( authenticationPanel, BorderLayout.CENTER );
final JCheckBox incognitoCheckBox = new JCheckBox( "Incognito" ); final JCheckBox incognitoCheckBox = new JCheckBox( "Incognito" );
incognitoCheckBox.setFont( Res.exoRegular().deriveFont( 12f ) );
incognitoCheckBox.setAlignmentX( LEFT_ALIGNMENT ); incognitoCheckBox.setAlignmentX( LEFT_ALIGNMENT );
incognitoCheckBox.setSelected( incognito ); incognitoCheckBox.setSelected( incognito );
incognitoCheckBox.addItemListener( new ItemListener() { incognitoCheckBox.addItemListener( new ItemListener() {

View File

@ -4,6 +4,7 @@ import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
import com.lyndir.masterpassword.MasterKey; import com.lyndir.masterpassword.MasterKey;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import org.jetbrains.annotations.NotNull;
/** /**
@ -26,6 +27,7 @@ public abstract class User {
return key != null || (masterPassword != null && !masterPassword.isEmpty()); return key != null || (masterPassword != null && !masterPassword.isEmpty());
} }
@NotNull
@Nonnull @Nonnull
public MasterKey getKey() { public MasterKey getKey() {
if (key == null) { if (key == null) {
@ -46,4 +48,8 @@ public abstract class User {
public String toString() { public String toString() {
return getFullName(); return getFullName();
} }
public abstract Iterable<Site> findSitesByName(final String siteName);
public abstract void addSite(final Site site);
} }

View File

@ -6,17 +6,19 @@ import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
import com.lyndir.masterpassword.*; import com.lyndir.masterpassword.*;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.Instant;
/** /**
* @author lhunath, 14-12-05 * @author lhunath, 14-12-05
*/ */
public class MPSite { public class MPSite {
public static final MPSiteType DEFAULT_TYPE = MPSiteType.GeneratedLong;
public static final int DEFAULT_COUNTER = 1; public static final MPSiteType DEFAULT_TYPE = MPSiteType.GeneratedLong;
public static final int DEFAULT_COUNTER = 1;
private int mpVersion; private int mpVersion;
private DateTime lastUsed; private Instant lastUsed;
private String siteName; private String siteName;
private MPSiteType siteType; private MPSiteType siteType;
private int siteCounter; private int siteCounter;
@ -28,12 +30,14 @@ public class MPSite {
} }
public MPSite(final String siteName, final MPSiteType siteType, final int siteCounter) { public MPSite(final String siteName, final MPSiteType siteType, final int siteCounter) {
this.mpVersion = MasterKey.ALGORITHM;
this.lastUsed = new Instant();
this.siteName = siteName; this.siteName = siteName;
this.siteType = siteType; this.siteType = siteType;
this.siteCounter = siteCounter; this.siteCounter = siteCounter;
} }
protected MPSite(final int mpVersion, final DateTime lastUsed, final String siteName, final MPSiteType siteType, final int siteCounter, protected MPSite(final int mpVersion, final Instant lastUsed, final String siteName, final MPSiteType siteType, final int siteCounter,
final int uses, final String loginName, final String importContent) { final int uses, final String loginName, final String importContent) {
this.mpVersion = mpVersion; this.mpVersion = mpVersion;
this.lastUsed = lastUsed; this.lastUsed = lastUsed;
@ -65,11 +69,11 @@ public class MPSite {
this.mpVersion = mpVersion; this.mpVersion = mpVersion;
} }
public DateTime getLastUsed() { public Instant getLastUsed() {
return lastUsed; return lastUsed;
} }
public void setLastUsed(final DateTime lastUsed) { public void setLastUsed(final Instant lastUsed) {
this.lastUsed = lastUsed; this.lastUsed = lastUsed;
} }

View File

@ -17,7 +17,7 @@ import org.joda.time.format.ISODateTimeFormat;
*/ */
public class MPSiteMarshaller { public class MPSiteMarshaller {
private static final DateTimeFormatter rfc3339 = ISODateTimeFormat.dateTime(); private static final DateTimeFormatter rfc3339 = ISODateTimeFormat.dateTimeNoMillis();
private final StringBuilder export = new StringBuilder(); private final StringBuilder export = new StringBuilder();
private ContentMode contentMode = ContentMode.PROTECTED; private ContentMode contentMode = ContentMode.PROTECTED;
@ -77,10 +77,10 @@ public class MPSiteMarshaller {
} }
public String marshallSite(MPSite site) { public String marshallSite(MPSite site) {
String exportLine = strf( "%s %8ld %8s %25s\t%25s\t%s", // String exportLine = strf( "%s %8d %8s %25s\t%25s\t%s", //
rfc3339.print( site.getLastUsed() ), // lastUsed rfc3339.print( site.getLastUsed() ), // lastUsed
site.getUses(), // uses site.getUses(), // uses
strf( "%lu:%lu:%lu", // strf( "%d:%d:%d", //
site.getSiteType().getMask(), // type site.getSiteType().getMask(), // type
site.getMPVersion(), // algorithm site.getMPVersion(), // algorithm
site.getSiteCounter() ), // counter site.getSiteCounter() ), // counter

View File

@ -29,7 +29,7 @@ public class MPSiteUnmarshaller {
@SuppressWarnings("UnusedDeclaration") @SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MPSite.class ); private static final Logger logger = Logger.get( MPSite.class );
private static final DateTimeFormatter rfc3339 = ISODateTimeFormat.dateTime(); private static final DateTimeFormatter rfc3339 = ISODateTimeFormat.dateTimeNoMillis();
private static final Pattern[] unmarshallFormats = new Pattern[]{ private static final Pattern[] unmarshallFormats = new Pattern[]{
Pattern.compile( "^([^ ]+) +(\\d+) +(\\d+)(:\\d+)? +([^\t]+)\t(.*)" ), Pattern.compile( "^([^ ]+) +(\\d+) +(\\d+)(:\\d+)? +([^\t]+)\t(.*)" ),
Pattern.compile( "^([^ ]+) +(\\d+) +(\\d+)(:\\d+)?(:\\d+)? +([^\t]*)\t *([^\t]+)\t(.*)" ) }; Pattern.compile( "^([^ ]+) +(\\d+) +(\\d+)(:\\d+)?(:\\d+)? +([^\t]*)\t *([^\t]+)\t(.*)" ) };
@ -124,7 +124,7 @@ public class MPSiteUnmarshaller {
switch (importFormat) { switch (importFormat) {
case 0: case 0:
site = new MPSite( ConversionUtils.toIntegerNN( siteMatcher.group( 4 ).replace( ":", "" ) ), // site = new MPSite( ConversionUtils.toIntegerNN( siteMatcher.group( 4 ).replace( ":", "" ) ), //
rfc3339.parseDateTime( siteMatcher.group( 1 ) ), // rfc3339.parseDateTime( siteMatcher.group( 1 ) ).toInstant(), //
siteMatcher.group( 5 ), // siteMatcher.group( 5 ), //
Iterables.getOnlyElement( MPSiteType.forMask( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ) ), Iterables.getOnlyElement( MPSiteType.forMask( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ) ),
MPSite.DEFAULT_COUNTER, // MPSite.DEFAULT_COUNTER, //
@ -135,7 +135,7 @@ public class MPSiteUnmarshaller {
case 1: case 1:
site = new MPSite( ConversionUtils.toIntegerNN( siteMatcher.group( 4 ).replace( ":", "" ) ), // site = new MPSite( ConversionUtils.toIntegerNN( siteMatcher.group( 4 ).replace( ":", "" ) ), //
rfc3339.parseDateTime( siteMatcher.group( 1 ) ), // rfc3339.parseDateTime( siteMatcher.group( 1 ) ).toInstant(), //
siteMatcher.group( 7 ), // siteMatcher.group( 7 ), //
Iterables.getOnlyElement( MPSiteType.forMask( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ) ), Iterables.getOnlyElement( MPSiteType.forMask( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ) ),
ConversionUtils.toIntegerNN( siteMatcher.group( 5 ).replace( ":", "" ) ), // ConversionUtils.toIntegerNN( siteMatcher.group( 5 ).replace( ":", "" ) ), //

View File

@ -20,6 +20,7 @@ public class MPUserFileManager extends MPUserManager {
private final File userFilesDirectory; private final File userFilesDirectory;
public static MPUserFileManager get() { public static MPUserFileManager get() {
MPUserManager.instance = instance;
return instance; return instance;
} }

View File

@ -11,6 +11,11 @@ import java.util.*;
public abstract class MPUserManager { public abstract class MPUserManager {
private final Map<String, MPUser> usersByName = Maps.newHashMap(); private final Map<String, MPUser> usersByName = Maps.newHashMap();
static MPUserManager instance;
public static MPUserManager get() {
return instance;
}
public MPUserManager(final Iterable<MPUser> users) { public MPUserManager(final Iterable<MPUser> users) {
for (MPUser user : users) for (MPUser user : users)