WIP - integrate user and site storage through export files into Java Swing GUI.
This commit is contained in:
parent
4ff8cd6d90
commit
84b624aea2
@ -181,10 +181,14 @@ public enum MPSiteType {
|
|||||||
*/
|
*/
|
||||||
public static ImmutableList<MPSiteType> forMask(final int mask) {
|
public static ImmutableList<MPSiteType> forMask(final int mask) {
|
||||||
|
|
||||||
|
int typeIndex = mask & 0xF, typeMask = mask & ~0xF;
|
||||||
|
|
||||||
ImmutableList.Builder<MPSiteType> types = ImmutableList.builder();
|
ImmutableList.Builder<MPSiteType> types = ImmutableList.builder();
|
||||||
for (MPSiteType siteType : values())
|
for (MPSiteType siteType : values()) {
|
||||||
if ((siteType.getMask() & mask) != 0)
|
int siteMask = siteType.getMask(), siteTypeIndex = siteMask & 0xF, siteTypeMask = siteMask & ~0xF;
|
||||||
|
if ((siteTypeMask & typeMask) != 0 && (typeIndex == 0 || siteTypeIndex == typeIndex))
|
||||||
types.add( siteType );
|
types.add( siteType );
|
||||||
|
}
|
||||||
|
|
||||||
return types.build();
|
return types.build();
|
||||||
}
|
}
|
||||||
|
@ -52,13 +52,14 @@ public class MasterKey {
|
|||||||
String mpKeyScope = MPSiteVariant.Password.getScope();
|
String mpKeyScope = MPSiteVariant.Password.getScope();
|
||||||
byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MP_charset ), userNameLengthBytes, userNameBytes );
|
byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MP_charset ), userNameLengthBytes, userNameBytes );
|
||||||
logger.trc( "key scope: %s", mpKeyScope );
|
logger.trc( "key scope: %s", mpKeyScope );
|
||||||
logger.trc( "masterKeySalt ID: %s", idForBytes( masterKeySalt ) );
|
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
|
||||||
|
|
||||||
try {
|
try {
|
||||||
masterKey = SCrypt.scrypt( masterPassword.getBytes( MP_charset ), masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
|
masterKey = SCrypt.scrypt( masterPassword.getBytes( MP_charset ), masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
|
||||||
valid = true;
|
valid = true;
|
||||||
|
|
||||||
logger.trc( "masterKey ID: %s (derived in %.2fs)", idForBytes( masterKey ), (System.currentTimeMillis() - start) / 1000D );
|
logger.trc( "masterKey ID: %s (derived in %.2fs)", CodeUtils.encodeHex( idForBytes( masterKey ) ),
|
||||||
|
(System.currentTimeMillis() - start) / 1000D );
|
||||||
}
|
}
|
||||||
catch (GeneralSecurityException e) {
|
catch (GeneralSecurityException e) {
|
||||||
throw logger.bug( e );
|
throw logger.bug( e );
|
||||||
@ -70,7 +71,7 @@ public class MasterKey {
|
|||||||
return fullName;
|
return fullName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getKeyID() {
|
public byte[] getKeyID() {
|
||||||
|
|
||||||
Preconditions.checkState( valid );
|
Preconditions.checkState( valid );
|
||||||
return idForBytes( masterKey );
|
return idForBytes( masterKey );
|
||||||
@ -111,10 +112,10 @@ public class MasterKey {
|
|||||||
siteContext == null? "(null)": siteContext );
|
siteContext == null? "(null)": siteContext );
|
||||||
|
|
||||||
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
|
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
|
||||||
logger.trc( "sitePasswordInfo ID: %s", idForBytes( sitePasswordInfo ) );
|
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
|
||||||
|
|
||||||
byte[] sitePasswordSeed = MP_mac.of( masterKey, sitePasswordInfo );
|
byte[] sitePasswordSeed = MP_mac.of( masterKey, sitePasswordInfo );
|
||||||
logger.trc( "sitePasswordSeed ID: %s", idForBytes( sitePasswordSeed ) );
|
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
|
||||||
|
|
||||||
Preconditions.checkState( sitePasswordSeed.length > 0 );
|
Preconditions.checkState( sitePasswordSeed.length > 0 );
|
||||||
int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign.
|
int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign.
|
||||||
@ -145,7 +146,7 @@ public class MasterKey {
|
|||||||
return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MP_byteOrder ).putInt( integer ).array();
|
return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MP_byteOrder ).putInt( integer ).array();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String idForBytes(final byte[] bytes) {
|
private static byte[] idForBytes(final byte[] bytes) {
|
||||||
return CodeUtils.encodeHex( MP_hash.of( bytes ) );
|
return MP_hash.of( bytes );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
<configuration>
|
<configuration>
|
||||||
<transformers>
|
<transformers>
|
||||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||||
<mainClass>com.lyndir.masterpassword.GUI</mainClass>
|
<mainClass>com.lyndir.masterpassword.gui.GUI</mainClass>
|
||||||
</transformer>
|
</transformer>
|
||||||
</transformers>
|
</transformers>
|
||||||
<filters>
|
<filters>
|
||||||
|
@ -1,164 +0,0 @@
|
|||||||
package com.lyndir.masterpassword;
|
|
||||||
|
|
||||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
|
|
||||||
|
|
||||||
import com.google.common.base.Splitter;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
import com.google.common.io.CharStreams;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.*;
|
|
||||||
import java.io.*;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.NoSuchElementException;
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.event.DocumentEvent;
|
|
||||||
import javax.swing.event.DocumentListener;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lhunath, 2014-06-11
|
|
||||||
*/
|
|
||||||
public class ConfigAuthenticationPanel extends AuthenticationPanel implements ItemListener, ActionListener, DocumentListener {
|
|
||||||
|
|
||||||
private final JComboBox userField;
|
|
||||||
private final JLabel masterPasswordLabel;
|
|
||||||
private final JPasswordField masterPasswordField;
|
|
||||||
|
|
||||||
public ConfigAuthenticationPanel(final UnlockFrame unlockFrame) {
|
|
||||||
|
|
||||||
// User
|
|
||||||
super( unlockFrame );
|
|
||||||
JLabel userLabel = new JLabel( "User:" );
|
|
||||||
userLabel.setAlignmentX( LEFT_ALIGNMENT );
|
|
||||||
userLabel.setHorizontalAlignment( SwingConstants.CENTER );
|
|
||||||
userLabel.setVerticalAlignment( SwingConstants.BOTTOM );
|
|
||||||
add( userLabel );
|
|
||||||
|
|
||||||
userField = new JComboBox<User>( new DefaultComboBoxModel<>( readConfigUsers() ) ) {
|
|
||||||
@Override
|
|
||||||
public Dimension getMaximumSize() {
|
|
||||||
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
userField.setAlignmentX( LEFT_ALIGNMENT );
|
|
||||||
userField.addItemListener( this );
|
|
||||||
userField.addActionListener( this );
|
|
||||||
add( userField );
|
|
||||||
|
|
||||||
// Master Password
|
|
||||||
masterPasswordLabel = new JLabel( "Master Password:" );
|
|
||||||
masterPasswordLabel.setAlignmentX( Component.LEFT_ALIGNMENT );
|
|
||||||
masterPasswordLabel.setHorizontalAlignment( SwingConstants.CENTER );
|
|
||||||
masterPasswordLabel.setVerticalAlignment( SwingConstants.BOTTOM );
|
|
||||||
add( masterPasswordLabel );
|
|
||||||
|
|
||||||
masterPasswordField = new JPasswordField() {
|
|
||||||
@Override
|
|
||||||
public Dimension getMaximumSize() {
|
|
||||||
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
masterPasswordField.setAlignmentX( Component.LEFT_ALIGNMENT );
|
|
||||||
masterPasswordField.addActionListener( this );
|
|
||||||
masterPasswordField.getDocument().addDocumentListener( this );
|
|
||||||
add( masterPasswordField );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Component getFocusComponent() {
|
|
||||||
return masterPasswordField.isVisible()? masterPasswordField: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void updateUser(boolean repack) {
|
|
||||||
boolean masterPasswordMissing = userField.getSelectedItem() == null || !((User) userField.getSelectedItem()).hasKey();
|
|
||||||
if (masterPasswordField.isVisible() != masterPasswordMissing) {
|
|
||||||
masterPasswordLabel.setVisible( masterPasswordMissing );
|
|
||||||
masterPasswordField.setVisible( masterPasswordMissing );
|
|
||||||
repack = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
super.updateUser( repack );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected User getUser() {
|
|
||||||
User selectedUser = (User) userField.getSelectedItem();
|
|
||||||
if (selectedUser.hasKey()) {
|
|
||||||
return selectedUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new User( selectedUser.getUserName(), new String( masterPasswordField.getPassword() ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getHelpText() {
|
|
||||||
return "Reads users from ~/.mpw, the following syntax applies:\nUser Name:masterpassword"
|
|
||||||
+ "\n\nEnsure the file's permissions make it only readable by you!";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean hasConfigUsers() {
|
|
||||||
return new File( System.getProperty( "user.home" ), ".mpw" ).canRead();
|
|
||||||
}
|
|
||||||
|
|
||||||
private User[] readConfigUsers() {
|
|
||||||
ImmutableList.Builder<User> users = ImmutableList.builder();
|
|
||||||
File mpwConfig = new File( System.getProperty( "user.home" ), ".mpw" );
|
|
||||||
try (FileReader mpwReader = new FileReader( mpwConfig )) {
|
|
||||||
for (String line : CharStreams.readLines( mpwReader )) {
|
|
||||||
if (line.startsWith( "#" ) || line.startsWith( "//" ) || line.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterator<String> fields = Splitter.on( ':' ).limit( 2 ).split( line ).iterator();
|
|
||||||
String userName = fields.next(), masterPassword = fields.next();
|
|
||||||
users.add( new User( userName, masterPassword ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
return Iterables.toArray( users.build(), User.class );
|
|
||||||
}
|
|
||||||
catch (FileNotFoundException e) {
|
|
||||||
JOptionPane.showMessageDialog( this, "First create the config file at:\n" + mpwConfig.getAbsolutePath() +
|
|
||||||
"\n\nIt should contain a line for each user of the following format:" +
|
|
||||||
"\nUser Name:masterpassword" +
|
|
||||||
"\n\nEnsure the file's permissions make it only readable by you!", //
|
|
||||||
"Config File Not Found", JOptionPane.WARNING_MESSAGE );
|
|
||||||
return new User[0];
|
|
||||||
}
|
|
||||||
catch (IOException | NoSuchElementException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
String error = ifNotNullElse( e.getLocalizedMessage(), ifNotNullElse( e.getMessage(), e.toString() ) );
|
|
||||||
JOptionPane.showMessageDialog( this, //
|
|
||||||
"Problem reading config file:\n" + mpwConfig.getAbsolutePath() //
|
|
||||||
+ "\n\n" + error, //
|
|
||||||
"Config File Not Readable", JOptionPane.WARNING_MESSAGE );
|
|
||||||
return new User[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void itemStateChanged(final ItemEvent e) {
|
|
||||||
updateUser( false );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(final ActionEvent e) {
|
|
||||||
updateUser( false );
|
|
||||||
unlockFrame.trySignIn( userField );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void insertUpdate(final DocumentEvent e) {
|
|
||||||
updateUser( false );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeUpdate(final DocumentEvent e) {
|
|
||||||
updateUser( false );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void changedUpdate(final DocumentEvent e) {
|
|
||||||
updateUser( false );
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
package com.lyndir.masterpassword;
|
|
||||||
|
|
||||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lhunath, 2014-06-08
|
|
||||||
*/
|
|
||||||
public class User {
|
|
||||||
|
|
||||||
private final String userName;
|
|
||||||
private final String masterPassword;
|
|
||||||
private MasterKey key;
|
|
||||||
|
|
||||||
public User(final String userName, final String masterPassword) {
|
|
||||||
this.userName = userName;
|
|
||||||
this.masterPassword = masterPassword;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUserName() {
|
|
||||||
return userName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasKey() {
|
|
||||||
return key != null || (masterPassword != null && !masterPassword.isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
public MasterKey getKey() {
|
|
||||||
if (key == null) {
|
|
||||||
if (!hasKey()) {
|
|
||||||
throw new IllegalStateException( strf( "Master password unknown for user: %s", userName ) );
|
|
||||||
} else {
|
|
||||||
key = new MasterKey( userName, masterPassword );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return userName.hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return userName;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
package com.lyndir.masterpassword;
|
package com.lyndir.masterpassword.gui;
|
||||||
|
|
||||||
import com.apple.eawt.*;
|
import com.apple.eawt.*;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
@ -1,5 +1,6 @@
|
|||||||
package com.lyndir.masterpassword;
|
package com.lyndir.masterpassword.gui;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
@ -41,7 +42,7 @@ public abstract class AuthenticationPanel extends JPanel {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getHelpText() {
|
public Iterable<? extends JButton> getButtons() {
|
||||||
return null;
|
return ImmutableList.of();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.lyndir.masterpassword;
|
package com.lyndir.masterpassword.gui;
|
||||||
|
|
||||||
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
|
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
|
||||||
|
|
@ -15,19 +15,16 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
package com.lyndir.masterpassword;
|
package com.lyndir.masterpassword.gui;
|
||||||
|
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
import com.google.common.io.*;
|
import com.google.common.io.*;
|
||||||
import com.lyndir.lhunath.opal.system.CodeUtils;
|
|
||||||
import com.lyndir.lhunath.opal.system.MessageDigests;
|
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
import com.lyndir.lhunath.opal.system.util.TypeUtils;
|
import com.lyndir.lhunath.opal.system.util.TypeUtils;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.List;
|
|
||||||
import java.util.jar.*;
|
import java.util.jar.*;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
@ -94,20 +91,18 @@ public class GUI implements UnlockFrame.SignInCallback {
|
|||||||
SwingUtilities.invokeLater( new Runnable() {
|
SwingUtilities.invokeLater( new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (passwordFrame == null) {
|
if (passwordFrame == null)
|
||||||
unlockFrame.setVisible( true );
|
unlockFrame.setVisible( true );
|
||||||
} else {
|
else
|
||||||
passwordFrame.setVisible( true );
|
passwordFrame.setVisible( true );
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean signedIn(final User user) {
|
public boolean signedIn(final User user) {
|
||||||
if (!user.hasKey()) {
|
if (!user.hasKey())
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
user.getKey();
|
user.getKey();
|
||||||
|
|
||||||
passwordFrame = newPasswordFrame( user );
|
passwordFrame = newPasswordFrame( user );
|
@ -1,4 +1,4 @@
|
|||||||
package com.lyndir.masterpassword;
|
package com.lyndir.masterpassword.gui;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
@ -11,35 +11,35 @@ import javax.swing.event.DocumentListener;
|
|||||||
/**
|
/**
|
||||||
* @author lhunath, 2014-06-11
|
* @author lhunath, 2014-06-11
|
||||||
*/
|
*/
|
||||||
public class TextAuthenticationPanel extends AuthenticationPanel implements DocumentListener, ActionListener {
|
public class IncognitoAuthenticationPanel extends AuthenticationPanel implements DocumentListener, ActionListener {
|
||||||
|
|
||||||
private final JTextField userNameField;
|
private final JTextField fullNameField;
|
||||||
private final JPasswordField masterPasswordField;
|
private final JPasswordField masterPasswordField;
|
||||||
|
|
||||||
public TextAuthenticationPanel(final UnlockFrame unlockFrame) {
|
public IncognitoAuthenticationPanel(final UnlockFrame unlockFrame) {
|
||||||
|
|
||||||
// User Name
|
// Full Name
|
||||||
super( unlockFrame );
|
super( unlockFrame );
|
||||||
JLabel userNameLabel = new JLabel( "User Name:" );
|
JLabel fullNameLabel = new JLabel( "Full Name:" );
|
||||||
userNameLabel.setAlignmentX( Component.LEFT_ALIGNMENT );
|
fullNameLabel.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
userNameLabel.setHorizontalAlignment( SwingConstants.CENTER );
|
fullNameLabel.setHorizontalAlignment( SwingConstants.CENTER );
|
||||||
userNameLabel.setVerticalAlignment( SwingConstants.BOTTOM );
|
fullNameLabel.setVerticalAlignment( SwingConstants.BOTTOM );
|
||||||
add( userNameLabel );
|
add( fullNameLabel );
|
||||||
|
|
||||||
userNameField = new JTextField() {
|
fullNameField = new JTextField() {
|
||||||
@Override
|
@Override
|
||||||
public Dimension getMaximumSize() {
|
public Dimension getMaximumSize() {
|
||||||
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
|
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
userNameField.setAlignmentX( Component.LEFT_ALIGNMENT );
|
fullNameField.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
userNameField.getDocument().addDocumentListener( this );
|
fullNameField.getDocument().addDocumentListener( this );
|
||||||
userNameField.addActionListener( this );
|
fullNameField.addActionListener( this );
|
||||||
add( userNameField );
|
add( fullNameField );
|
||||||
|
|
||||||
// Master Password
|
// Master Password
|
||||||
JLabel masterPasswordLabel = new JLabel( "Master Password:" );
|
JLabel masterPasswordLabel = new JLabel( "Master Password:" );
|
||||||
masterPasswordLabel.setAlignmentX( Component.LEFT_ALIGNMENT );
|
masterPasswordLabel.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
masterPasswordLabel.setHorizontalAlignment( SwingConstants.CENTER );
|
masterPasswordLabel.setHorizontalAlignment( SwingConstants.CENTER );
|
||||||
masterPasswordLabel.setVerticalAlignment( SwingConstants.BOTTOM );
|
masterPasswordLabel.setVerticalAlignment( SwingConstants.BOTTOM );
|
||||||
add( masterPasswordLabel );
|
add( masterPasswordLabel );
|
||||||
@ -50,7 +50,7 @@ public class TextAuthenticationPanel extends AuthenticationPanel implements Docu
|
|||||||
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
|
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
masterPasswordField.setAlignmentX( Component.LEFT_ALIGNMENT );
|
masterPasswordField.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
masterPasswordField.addActionListener( this );
|
masterPasswordField.addActionListener( this );
|
||||||
masterPasswordField.getDocument().addDocumentListener( this );
|
masterPasswordField.getDocument().addDocumentListener( this );
|
||||||
add( masterPasswordField );
|
add( masterPasswordField );
|
||||||
@ -58,12 +58,12 @@ public class TextAuthenticationPanel extends AuthenticationPanel implements Docu
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Component getFocusComponent() {
|
public Component getFocusComponent() {
|
||||||
return userNameField;
|
return fullNameField;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected User getUser() {
|
protected User getUser() {
|
||||||
return new User( userNameField.getText(), new String( masterPasswordField.getPassword() ) );
|
return new IncognitoUser( fullNameField.getText(), new String( masterPasswordField.getPassword() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -84,6 +84,6 @@ public class TextAuthenticationPanel extends AuthenticationPanel implements Docu
|
|||||||
@Override
|
@Override
|
||||||
public void actionPerformed(final ActionEvent e) {
|
public void actionPerformed(final ActionEvent e) {
|
||||||
updateUser( false );
|
updateUser( false );
|
||||||
unlockFrame.trySignIn( userNameField, masterPasswordField );
|
unlockFrame.trySignIn( fullNameField, masterPasswordField );
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package com.lyndir.masterpassword.gui;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lhunath, 2014-06-08
|
||||||
|
*/
|
||||||
|
public class IncognitoUser extends User {
|
||||||
|
|
||||||
|
private final String fullName;
|
||||||
|
private final String masterPassword;
|
||||||
|
|
||||||
|
public IncognitoUser(final String fullName, final String masterPassword) {
|
||||||
|
this.fullName = fullName;
|
||||||
|
this.masterPassword = masterPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFullName() {
|
||||||
|
return fullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getMasterPassword() {
|
||||||
|
return masterPassword;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,163 @@
|
|||||||
|
package com.lyndir.masterpassword.gui;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.collect.*;
|
||||||
|
import com.lyndir.masterpassword.model.MPUser;
|
||||||
|
import com.lyndir.masterpassword.model.MPUserFileManager;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.*;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.event.DocumentEvent;
|
||||||
|
import javax.swing.event.DocumentListener;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lhunath, 2014-06-11
|
||||||
|
*/
|
||||||
|
public class ModelAuthenticationPanel extends AuthenticationPanel implements ItemListener, ActionListener, DocumentListener {
|
||||||
|
|
||||||
|
private final JComboBox<ModelUser> userField;
|
||||||
|
private final JLabel masterPasswordLabel;
|
||||||
|
private final JPasswordField masterPasswordField;
|
||||||
|
|
||||||
|
public ModelAuthenticationPanel(final UnlockFrame unlockFrame) {
|
||||||
|
|
||||||
|
// User
|
||||||
|
super( unlockFrame );
|
||||||
|
JLabel userLabel = new JLabel( "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.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
|
userField.addItemListener( this );
|
||||||
|
userField.addActionListener( this );
|
||||||
|
add( userField );
|
||||||
|
|
||||||
|
// Master Password
|
||||||
|
masterPasswordLabel = new JLabel( "Master Password:" );
|
||||||
|
masterPasswordLabel.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
|
masterPasswordLabel.setHorizontalAlignment( SwingConstants.CENTER );
|
||||||
|
masterPasswordLabel.setVerticalAlignment( SwingConstants.BOTTOM );
|
||||||
|
add( masterPasswordLabel );
|
||||||
|
|
||||||
|
masterPasswordField = new JPasswordField() {
|
||||||
|
@Override
|
||||||
|
public Dimension getMaximumSize() {
|
||||||
|
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
masterPasswordField.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
|
masterPasswordField.addActionListener( this );
|
||||||
|
masterPasswordField.getDocument().addDocumentListener( this );
|
||||||
|
add( masterPasswordField );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getFocusComponent() {
|
||||||
|
return masterPasswordField.isVisible()? masterPasswordField: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateUser(boolean repack) {
|
||||||
|
int selectedIndex = userField.getSelectedIndex();
|
||||||
|
if (selectedIndex >= 0) {
|
||||||
|
ModelUser selectedUser = userField.getModel().getElementAt( selectedIndex );
|
||||||
|
boolean showPasswordField = !selectedUser.keySaved();
|
||||||
|
if (masterPasswordField.isVisible() != showPasswordField) {
|
||||||
|
masterPasswordLabel.setVisible( showPasswordField );
|
||||||
|
masterPasswordField.setVisible( showPasswordField );
|
||||||
|
repack = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.updateUser( repack );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected User getUser() {
|
||||||
|
int selectedIndex = userField.getSelectedIndex();
|
||||||
|
if (selectedIndex < 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
ModelUser selectedUser = userField.getModel().getElementAt( selectedIndex );
|
||||||
|
if (selectedUser != null)
|
||||||
|
selectedUser.setMasterPassword( new String( masterPasswordField.getPassword() ) );
|
||||||
|
|
||||||
|
return selectedUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<? extends JButton> getButtons() {
|
||||||
|
return ImmutableList.of( new JButton( Res.iconAdd() ) {
|
||||||
|
{
|
||||||
|
addActionListener( new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(final ActionEvent e) {
|
||||||
|
String fullName = JOptionPane.showInputDialog( ModelAuthenticationPanel.this, //
|
||||||
|
"Enter your full name, ensuring it is correctly spelled and capitalized:",
|
||||||
|
"New User", JOptionPane.QUESTION_MESSAGE );
|
||||||
|
MPUserFileManager.get().addUser( new MPUser( fullName ) );
|
||||||
|
userField.setModel( new DefaultComboBoxModel<>( readConfigUsers() ) );
|
||||||
|
updateUser( true );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}, new JButton( Res.iconQuestion() ) {
|
||||||
|
{
|
||||||
|
addActionListener( new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(final ActionEvent e) {
|
||||||
|
JOptionPane.showMessageDialog( ModelAuthenticationPanel.this, //
|
||||||
|
"Reads users and sites from the directory at ~/.mpw.", //
|
||||||
|
"Help", JOptionPane.INFORMATION_MESSAGE );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
private ModelUser[] readConfigUsers() {
|
||||||
|
return FluentIterable.from( MPUserFileManager.get().getUsers() ).transform( new Function<MPUser, ModelUser>() {
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public ModelUser apply(final MPUser model) {
|
||||||
|
return new ModelUser( model );
|
||||||
|
}
|
||||||
|
} ).toArray( ModelUser.class );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void itemStateChanged(final ItemEvent e) {
|
||||||
|
updateUser( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(final ActionEvent e) {
|
||||||
|
updateUser( false );
|
||||||
|
unlockFrame.trySignIn( userField );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insertUpdate(final DocumentEvent e) {
|
||||||
|
updateUser( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeUpdate(final DocumentEvent e) {
|
||||||
|
updateUser( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changedUpdate(final DocumentEvent e) {
|
||||||
|
updateUser( false );
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
package com.lyndir.masterpassword.gui;
|
||||||
|
|
||||||
|
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
|
||||||
|
|
||||||
|
import com.lyndir.masterpassword.MasterKey;
|
||||||
|
import com.lyndir.masterpassword.model.MPUser;
|
||||||
|
import com.lyndir.masterpassword.model.MPUserFileManager;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lhunath, 14-12-08
|
||||||
|
*/
|
||||||
|
public class ModelUser extends User {
|
||||||
|
|
||||||
|
private final MPUser user;
|
||||||
|
private String masterPassword;
|
||||||
|
|
||||||
|
public ModelUser(MPUser user) {
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFullName() {
|
||||||
|
return user.getFullName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getMasterPassword() {
|
||||||
|
return masterPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMasterPassword(final String masterPassword) {
|
||||||
|
this.masterPassword = masterPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MasterKey getKey() {
|
||||||
|
MasterKey key = super.getKey();
|
||||||
|
if (!user.hasKeyID()) {
|
||||||
|
user.setKeyID( key.getKeyID() );
|
||||||
|
MPUserFileManager.get().save();
|
||||||
|
}
|
||||||
|
else if (!user.hasKeyID( key.getKeyID() ))
|
||||||
|
throw new IllegalStateException( strf( "Incorrect master password for user: %s", getFullName() ) );
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean keySaved() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,9 @@
|
|||||||
package com.lyndir.masterpassword;
|
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.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;
|
||||||
@ -38,7 +39,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
|||||||
} );
|
} );
|
||||||
|
|
||||||
// User
|
// User
|
||||||
add( label = new JLabel( strf( "Generating passwords for: %s", user.getUserName() ) ), BorderLayout.NORTH );
|
add( label = new JLabel( strf( "Generating passwords for: %s", user.getFullName() ) ), BorderLayout.NORTH );
|
||||||
label.setFont( Res.exoRegular().deriveFont( 12f ) );
|
label.setFont( Res.exoRegular().deriveFont( 12f ) );
|
||||||
label.setAlignmentX( LEFT_ALIGNMENT );
|
label.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package com.lyndir.masterpassword;
|
package com.lyndir.masterpassword.gui;
|
||||||
|
|
||||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse;
|
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse;
|
||||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
||||||
@ -45,6 +45,10 @@ public abstract class Res {
|
|||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Icon iconAdd() {
|
||||||
|
return new RetinaIcon( Resources.getResource( "media/icon_add@2x.png" ) );
|
||||||
|
}
|
||||||
|
|
||||||
public static Icon iconQuestion() {
|
public static Icon iconQuestion() {
|
||||||
return new RetinaIcon( Resources.getResource( "media/icon_question@2x.png" ) );
|
return new RetinaIcon( Resources.getResource( "media/icon_question@2x.png" ) );
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.lyndir.masterpassword;
|
package com.lyndir.masterpassword.gui;
|
||||||
|
|
||||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
|
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ public class UnlockFrame extends JFrame {
|
|||||||
private final JPanel root;
|
private final JPanel root;
|
||||||
private final JButton signInButton;
|
private final JButton signInButton;
|
||||||
private final JPanel authenticationContainer;
|
private final JPanel authenticationContainer;
|
||||||
private boolean useConfig;
|
private boolean incognito;
|
||||||
public User user;
|
public User user;
|
||||||
|
|
||||||
public UnlockFrame(final SignInCallback signInCallback)
|
public UnlockFrame(final SignInCallback signInCallback)
|
||||||
@ -46,7 +46,6 @@ public class UnlockFrame extends JFrame {
|
|||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
useConfig = ConfigAuthenticationPanel.hasConfigUsers();
|
|
||||||
createAuthenticationPanel();
|
createAuthenticationPanel();
|
||||||
|
|
||||||
setLocationByPlatform( true );
|
setLocationByPlatform( true );
|
||||||
@ -65,21 +64,21 @@ public class UnlockFrame extends JFrame {
|
|||||||
authenticationContainer.removeAll();
|
authenticationContainer.removeAll();
|
||||||
|
|
||||||
final AuthenticationPanel authenticationPanel;
|
final AuthenticationPanel authenticationPanel;
|
||||||
if (useConfig) {
|
if (incognito) {
|
||||||
authenticationPanel = new ConfigAuthenticationPanel( this );
|
authenticationPanel = new IncognitoAuthenticationPanel( this );
|
||||||
} else {
|
} else {
|
||||||
authenticationPanel = new TextAuthenticationPanel( this );
|
authenticationPanel = new ModelAuthenticationPanel( this );
|
||||||
}
|
}
|
||||||
authenticationPanel.updateUser( false );
|
authenticationPanel.updateUser( false );
|
||||||
authenticationContainer.add( authenticationPanel, BorderLayout.CENTER );
|
authenticationContainer.add( authenticationPanel, BorderLayout.CENTER );
|
||||||
|
|
||||||
final JCheckBox typeCheckBox = new JCheckBox( "Use Config File" );
|
final JCheckBox incognitoCheckBox = new JCheckBox( "Incognito" );
|
||||||
typeCheckBox.setAlignmentX( LEFT_ALIGNMENT );
|
incognitoCheckBox.setAlignmentX( LEFT_ALIGNMENT );
|
||||||
typeCheckBox.setSelected( useConfig );
|
incognitoCheckBox.setSelected( incognito );
|
||||||
typeCheckBox.addItemListener( new ItemListener() {
|
incognitoCheckBox.addItemListener( new ItemListener() {
|
||||||
@Override
|
@Override
|
||||||
public void itemStateChanged(final ItemEvent e) {
|
public void itemStateChanged(final ItemEvent e) {
|
||||||
useConfig = typeCheckBox.isSelected();
|
incognito = incognitoCheckBox.isSelected();
|
||||||
SwingUtilities.invokeLater( new Runnable() {
|
SwingUtilities.invokeLater( new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@ -89,24 +88,15 @@ public class UnlockFrame extends JFrame {
|
|||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
JButton typeHelp = new JButton( Res.iconQuestion() );
|
JComponent toolsPanel = Components.boxLayout( BoxLayout.LINE_AXIS, incognitoCheckBox, Box.createGlue() );
|
||||||
typeHelp.setMargin( new Insets( 0, 0, 0, 0 ) );
|
toolsPanel.setAlignmentX( Component.LEFT_ALIGNMENT );
|
||||||
typeHelp.setBackground( Color.red );
|
authenticationContainer.add( toolsPanel );
|
||||||
typeHelp.setAlignmentX( RIGHT_ALIGNMENT );
|
for (JButton button : authenticationPanel.getButtons()) {
|
||||||
typeHelp.setBorder( null );
|
button.setMargin( new Insets( 0, 0, 0, 0 ) );
|
||||||
typeHelp.addActionListener( new ActionListener() {
|
button.setAlignmentX( RIGHT_ALIGNMENT );
|
||||||
@Override
|
button.setBorder( null );
|
||||||
public void actionPerformed(final ActionEvent e) {
|
toolsPanel.add( button );
|
||||||
JOptionPane.showMessageDialog( UnlockFrame.this, authenticationPanel.getHelpText(), "Help",
|
|
||||||
JOptionPane.INFORMATION_MESSAGE );
|
|
||||||
}
|
}
|
||||||
} );
|
|
||||||
if (authenticationPanel.getHelpText() == null) {
|
|
||||||
typeHelp.setVisible( false );
|
|
||||||
}
|
|
||||||
JComponent typePanel = Components.boxLayout( BoxLayout.LINE_AXIS, typeCheckBox, Box.createGlue(), typeHelp );
|
|
||||||
typePanel.setAlignmentX( Component.LEFT_ALIGNMENT );
|
|
||||||
authenticationContainer.add( typePanel );
|
|
||||||
|
|
||||||
checkSignIn();
|
checkSignIn();
|
||||||
validate();
|
validate();
|
||||||
@ -126,7 +116,7 @@ public class UnlockFrame extends JFrame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean checkSignIn() {
|
boolean checkSignIn() {
|
||||||
boolean enabled = user != null && !user.getUserName().isEmpty() && user.hasKey();
|
boolean enabled = user != null && !user.getFullName().isEmpty() && user.hasKey();
|
||||||
signInButton.setEnabled( enabled );
|
signInButton.setEnabled( enabled );
|
||||||
|
|
||||||
return enabled;
|
return enabled;
|
@ -0,0 +1,45 @@
|
|||||||
|
package com.lyndir.masterpassword.gui;
|
||||||
|
|
||||||
|
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
||||||
|
|
||||||
|
import com.lyndir.masterpassword.MasterKey;
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lhunath, 2014-06-08
|
||||||
|
*/
|
||||||
|
public abstract class User {
|
||||||
|
|
||||||
|
private MasterKey key;
|
||||||
|
|
||||||
|
public abstract String getFullName();
|
||||||
|
|
||||||
|
protected abstract String getMasterPassword();
|
||||||
|
|
||||||
|
public boolean hasKey() {
|
||||||
|
String masterPassword = getMasterPassword();
|
||||||
|
return key != null || (masterPassword != null && !masterPassword.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public MasterKey getKey() {
|
||||||
|
if (key == null) {
|
||||||
|
if (!hasKey())
|
||||||
|
throw new IllegalStateException( strf( "Master password unknown for user: %s", getFullName() ) );
|
||||||
|
key = new MasterKey( getFullName(), getMasterPassword() );
|
||||||
|
}
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return getFullName().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getFullName();
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.4 KiB |
@ -1,52 +0,0 @@
|
|||||||
package com.lyndir.masterpassword.model;
|
|
||||||
|
|
||||||
import com.google.common.io.CharSink;
|
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lhunath, 14-12-07
|
|
||||||
*/
|
|
||||||
public class MPSiteFileManager extends MPSiteManager {
|
|
||||||
|
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
|
||||||
private static final Logger logger = Logger.get( MPSiteFileManager.class );
|
|
||||||
|
|
||||||
private final File file;
|
|
||||||
|
|
||||||
public static MPSiteFileManager create(final File file) {
|
|
||||||
try {
|
|
||||||
return new MPSiteFileManager( file );
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
throw logger.bug( e, "Unable to open sites from file: %s", file );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected MPSiteFileManager(final File file)
|
|
||||||
throws IOException {
|
|
||||||
|
|
||||||
super( MPSiteUnmarshaller.unmarshall( file ).getUser() );
|
|
||||||
this.file = file;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void save() {
|
|
||||||
try {
|
|
||||||
new CharSink() {
|
|
||||||
@Override
|
|
||||||
public Writer openStream()
|
|
||||||
throws IOException {
|
|
||||||
return new FileWriter( file );
|
|
||||||
}
|
|
||||||
}.write( MPSiteMarshaller.marshallSafe( getUser() ).getExport() );
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
logger.err( e, "Unable to save sites to file: %s", file );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public File getFile() {
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
package com.lyndir.masterpassword.model;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lhunath, 14-12-05
|
|
||||||
*/
|
|
||||||
public abstract class MPSiteManager {
|
|
||||||
|
|
||||||
private final MPUser user;
|
|
||||||
|
|
||||||
public MPSiteManager(final MPUser user) {
|
|
||||||
this.user = user;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MPUser getUser() {
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<MPSiteResult> findSitesByName(String query) {
|
|
||||||
ImmutableList.Builder<MPSiteResult> results = ImmutableList.builder();
|
|
||||||
for (MPSite site : user.getSites())
|
|
||||||
if (site.getSiteName().startsWith( query ))
|
|
||||||
results.add( new MPSiteResult( site ) );
|
|
||||||
|
|
||||||
return results.build();
|
|
||||||
}
|
|
||||||
}
|
|
@ -31,9 +31,9 @@ public class MPSiteUnmarshaller {
|
|||||||
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.dateTime();
|
||||||
private static final Pattern[] unmarshallFormats = new Pattern[]{
|
private static final Pattern[] unmarshallFormats = new Pattern[]{
|
||||||
Pattern.compile( "^([^ ]+) +([[:digit:]]+) +([[:digit:]]+)(:[[:digit:]]+)? +([^\t]+)\t(.*)" ),
|
Pattern.compile( "^([^ ]+) +(\\d+) +(\\d+)(:\\d+)? +([^\t]+)\t(.*)" ),
|
||||||
Pattern.compile( "^([^ ]+) +([[:digit:]]+) +([[:digit:]]+)(:[[:digit:]]+)?(:[[:digit:]]+)? +([^\t]*)\t *([^\t]+)\t(.*)" ) };
|
Pattern.compile( "^([^ ]+) +(\\d+) +(\\d+)(:\\d+)?(:\\d+)? +([^\t]*)\t *([^\t]+)\t(.*)" ) };
|
||||||
private static final Pattern headerFormat = Pattern.compile( "^#[[:space:]]*([^:]+): (.*)" );
|
private static final Pattern headerFormat = Pattern.compile( "^#\\s*([^:]+): (.*)" );
|
||||||
|
|
||||||
private final int importFormat;
|
private final int importFormat;
|
||||||
private final int mpVersion;
|
private final int mpVersion;
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package com.lyndir.masterpassword.model;
|
package com.lyndir.masterpassword.model;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import com.lyndir.lhunath.opal.system.CodeUtils;
|
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||||
import com.lyndir.masterpassword.MPSiteType;
|
import com.lyndir.masterpassword.MPSiteType;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.*;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -13,17 +14,23 @@ import org.joda.time.DateTime;
|
|||||||
public class MPUser {
|
public class MPUser {
|
||||||
|
|
||||||
private final String fullName;
|
private final String fullName;
|
||||||
private final byte[] keyID;
|
|
||||||
private final int avatar;
|
|
||||||
private final MPSiteType defaultType;
|
|
||||||
private final DateTime lastUsed;
|
|
||||||
private final Collection<MPSite> sites = Sets.newHashSet();
|
private final Collection<MPSite> sites = Sets.newHashSet();
|
||||||
|
|
||||||
|
private byte[] keyID;
|
||||||
|
private int avatar;
|
||||||
|
private MPSiteType defaultType;
|
||||||
|
private ReadableInstant lastUsed;
|
||||||
|
|
||||||
|
public MPUser(final String fullName) {
|
||||||
|
this( fullName, null );
|
||||||
|
}
|
||||||
|
|
||||||
public MPUser(final String fullName, final byte[] keyID) {
|
public MPUser(final String fullName, final byte[] keyID) {
|
||||||
this( fullName, keyID, 0, MPSiteType.GeneratedLong, new DateTime() );
|
this( fullName, keyID, 0, MPSiteType.GeneratedLong, new DateTime() );
|
||||||
}
|
}
|
||||||
|
|
||||||
public MPUser(final String fullName, final byte[] keyID, final int avatar, final MPSiteType defaultType, final DateTime lastUsed) {
|
public MPUser(final String fullName, final byte[] keyID, final int avatar, final MPSiteType defaultType,
|
||||||
|
final ReadableInstant lastUsed) {
|
||||||
this.fullName = fullName;
|
this.fullName = fullName;
|
||||||
this.keyID = keyID;
|
this.keyID = keyID;
|
||||||
this.avatar = avatar;
|
this.avatar = avatar;
|
||||||
@ -31,6 +38,15 @@ public class MPUser {
|
|||||||
this.lastUsed = lastUsed;
|
this.lastUsed = lastUsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Collection<MPSiteResult> findSitesByName(String query) {
|
||||||
|
ImmutableList.Builder<MPSiteResult> results = ImmutableList.builder();
|
||||||
|
for (MPSite site : getSites())
|
||||||
|
if (site.getSiteName().startsWith( query ))
|
||||||
|
results.add( new MPSiteResult( site ) );
|
||||||
|
|
||||||
|
return results.build();
|
||||||
|
}
|
||||||
|
|
||||||
public void addSite(final MPSite site) {
|
public void addSite(final MPSite site) {
|
||||||
sites.add( site );
|
sites.add( site );
|
||||||
}
|
}
|
||||||
@ -39,6 +55,10 @@ public class MPUser {
|
|||||||
return fullName;
|
return fullName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasKeyID() {
|
||||||
|
return keyID != null;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasKeyID(final byte[] keyID) {
|
public boolean hasKeyID(final byte[] keyID) {
|
||||||
return Arrays.equals( this.keyID, keyID );
|
return Arrays.equals( this.keyID, keyID );
|
||||||
}
|
}
|
||||||
@ -47,18 +67,34 @@ public class MPUser {
|
|||||||
return CodeUtils.encodeHex( keyID );
|
return CodeUtils.encodeHex( keyID );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setKeyID(final byte[] keyID) {
|
||||||
|
this.keyID = keyID;
|
||||||
|
}
|
||||||
|
|
||||||
public int getAvatar() {
|
public int getAvatar() {
|
||||||
return avatar;
|
return avatar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAvatar(final int avatar) {
|
||||||
|
this.avatar = avatar;
|
||||||
|
}
|
||||||
|
|
||||||
public MPSiteType getDefaultType() {
|
public MPSiteType getDefaultType() {
|
||||||
return defaultType;
|
return defaultType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DateTime getLastUsed() {
|
public void setDefaultType(final MPSiteType defaultType) {
|
||||||
|
this.defaultType = defaultType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadableInstant getLastUsed() {
|
||||||
return lastUsed;
|
return lastUsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateLastUsed() {
|
||||||
|
this.lastUsed = new Instant();
|
||||||
|
}
|
||||||
|
|
||||||
public Iterable<MPSite> getSites() {
|
public Iterable<MPSite> getSites() {
|
||||||
return sites;
|
return sites;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
package com.lyndir.masterpassword.model;
|
||||||
|
|
||||||
|
import com.google.common.base.*;
|
||||||
|
import com.google.common.collect.*;
|
||||||
|
import com.google.common.io.CharSink;
|
||||||
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
|
import java.io.*;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lhunath, 14-12-07
|
||||||
|
*/
|
||||||
|
public class MPUserFileManager extends MPUserManager {
|
||||||
|
|
||||||
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
|
private static final Logger logger = Logger.get( MPUserFileManager.class );
|
||||||
|
private static final MPUserFileManager instance = create( new File( System.getProperty( "user.home" ), ".mpw" ) );
|
||||||
|
|
||||||
|
private final File userFilesDirectory;
|
||||||
|
|
||||||
|
public static MPUserFileManager get() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MPUserFileManager create(final File userFilesDirectory) {
|
||||||
|
return new MPUserFileManager( userFilesDirectory );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected MPUserFileManager(final File userFilesDirectory) {
|
||||||
|
|
||||||
|
super( unmarshallUsers( userFilesDirectory ) );
|
||||||
|
this.userFilesDirectory = userFilesDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Iterable<MPUser> unmarshallUsers(final File userFilesDirectory) {
|
||||||
|
if (!userFilesDirectory.mkdirs() && !userFilesDirectory.isDirectory()) {
|
||||||
|
logger.err( "Couldn't create directory for user files: %s", userFilesDirectory );
|
||||||
|
return ImmutableList.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
return FluentIterable.from( ImmutableList.copyOf( userFilesDirectory.listFiles( new FilenameFilter() {
|
||||||
|
@Override
|
||||||
|
public boolean accept(final File dir, final String name) {
|
||||||
|
return name.endsWith( ".mpsites" );
|
||||||
|
}
|
||||||
|
} ) ) ).transform( new Function<File, MPUser>() {
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public MPUser apply(final File file) {
|
||||||
|
try {
|
||||||
|
return MPSiteUnmarshaller.unmarshall( file ).getUser();
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
logger.err( e, "Couldn't read user from: %s", file );
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} ).filter( Predicates.notNull() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save() {
|
||||||
|
for (final MPUser user : getUsers())
|
||||||
|
try {
|
||||||
|
new CharSink() {
|
||||||
|
@Override
|
||||||
|
public Writer openStream()
|
||||||
|
throws IOException {
|
||||||
|
return new FileWriter( new File(userFilesDirectory, user.getFullName() + ".mpsites" ) );
|
||||||
|
}
|
||||||
|
}.write( MPSiteMarshaller.marshallSafe( user ).getExport() );
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
logger.err( e, "Unable to save sites for user: %s", user );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package com.lyndir.masterpassword.model;
|
||||||
|
|
||||||
|
import com.google.common.collect.FluentIterable;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lhunath, 14-12-05
|
||||||
|
*/
|
||||||
|
public abstract class MPUserManager {
|
||||||
|
|
||||||
|
private final Map<String, MPUser> usersByName = Maps.newHashMap();
|
||||||
|
|
||||||
|
public MPUserManager(final Iterable<MPUser> users) {
|
||||||
|
for (MPUser user : users)
|
||||||
|
addUser( user );
|
||||||
|
}
|
||||||
|
|
||||||
|
public SortedSet<MPUser> getUsers() {
|
||||||
|
return FluentIterable.from( usersByName.values() ).toSortedSet( new Comparator<MPUser>() {
|
||||||
|
@Override
|
||||||
|
public int compare(final MPUser user1, final MPUser user2) {
|
||||||
|
return user1.getLastUsed().compareTo( user2.getLastUsed() );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addUser(final MPUser user) {
|
||||||
|
usersByName.put( user.getFullName(), user );
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user