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) {
|
||||
|
||||
int typeIndex = mask & 0xF, typeMask = mask & ~0xF;
|
||||
|
||||
ImmutableList.Builder<MPSiteType> types = ImmutableList.builder();
|
||||
for (MPSiteType siteType : values())
|
||||
if ((siteType.getMask() & mask) != 0)
|
||||
for (MPSiteType siteType : values()) {
|
||||
int siteMask = siteType.getMask(), siteTypeIndex = siteMask & 0xF, siteTypeMask = siteMask & ~0xF;
|
||||
if ((siteTypeMask & typeMask) != 0 && (typeIndex == 0 || siteTypeIndex == typeIndex))
|
||||
types.add( siteType );
|
||||
}
|
||||
|
||||
return types.build();
|
||||
}
|
||||
|
@ -52,13 +52,14 @@ public class MasterKey {
|
||||
String mpKeyScope = MPSiteVariant.Password.getScope();
|
||||
byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MP_charset ), userNameLengthBytes, userNameBytes );
|
||||
logger.trc( "key scope: %s", mpKeyScope );
|
||||
logger.trc( "masterKeySalt ID: %s", idForBytes( masterKeySalt ) );
|
||||
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
|
||||
|
||||
try {
|
||||
masterKey = SCrypt.scrypt( masterPassword.getBytes( MP_charset ), masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
|
||||
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) {
|
||||
throw logger.bug( e );
|
||||
@ -70,7 +71,7 @@ public class MasterKey {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public String getKeyID() {
|
||||
public byte[] getKeyID() {
|
||||
|
||||
Preconditions.checkState( valid );
|
||||
return idForBytes( masterKey );
|
||||
@ -111,10 +112,10 @@ public class MasterKey {
|
||||
siteContext == null? "(null)": siteContext );
|
||||
|
||||
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 );
|
||||
logger.trc( "sitePasswordSeed ID: %s", idForBytes( sitePasswordSeed ) );
|
||||
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
|
||||
|
||||
Preconditions.checkState( sitePasswordSeed.length > 0 );
|
||||
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();
|
||||
}
|
||||
|
||||
private static String idForBytes(final byte[] bytes) {
|
||||
return CodeUtils.encodeHex( MP_hash.of( bytes ) );
|
||||
private static byte[] idForBytes(final byte[] bytes) {
|
||||
return MP_hash.of( bytes );
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,7 @@
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>com.lyndir.masterpassword.GUI</mainClass>
|
||||
<mainClass>com.lyndir.masterpassword.gui.GUI</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
<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 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 javax.swing.*;
|
||||
|
||||
@ -41,7 +42,7 @@ public abstract class AuthenticationPanel extends JPanel {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getHelpText() {
|
||||
return null;
|
||||
public Iterable<? extends JButton> getButtons() {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.lyndir.masterpassword;
|
||||
package com.lyndir.masterpassword.gui;
|
||||
|
||||
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.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.util.TypeUtils;
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.jar.*;
|
||||
import javax.swing.*;
|
||||
|
||||
@ -94,20 +91,18 @@ public class GUI implements UnlockFrame.SignInCallback {
|
||||
SwingUtilities.invokeLater( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (passwordFrame == null) {
|
||||
if (passwordFrame == null)
|
||||
unlockFrame.setVisible( true );
|
||||
} else {
|
||||
else
|
||||
passwordFrame.setVisible( true );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean signedIn(final User user) {
|
||||
if (!user.hasKey()) {
|
||||
if (!user.hasKey())
|
||||
return false;
|
||||
}
|
||||
user.getKey();
|
||||
|
||||
passwordFrame = newPasswordFrame( user );
|
@ -1,4 +1,4 @@
|
||||
package com.lyndir.masterpassword;
|
||||
package com.lyndir.masterpassword.gui;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
@ -11,35 +11,35 @@ import javax.swing.event.DocumentListener;
|
||||
/**
|
||||
* @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;
|
||||
|
||||
public TextAuthenticationPanel(final UnlockFrame unlockFrame) {
|
||||
public IncognitoAuthenticationPanel(final UnlockFrame unlockFrame) {
|
||||
|
||||
// User Name
|
||||
// Full Name
|
||||
super( unlockFrame );
|
||||
JLabel userNameLabel = new JLabel( "User Name:" );
|
||||
userNameLabel.setAlignmentX( Component.LEFT_ALIGNMENT );
|
||||
userNameLabel.setHorizontalAlignment( SwingConstants.CENTER );
|
||||
userNameLabel.setVerticalAlignment( SwingConstants.BOTTOM );
|
||||
add( userNameLabel );
|
||||
JLabel fullNameLabel = new JLabel( "Full Name:" );
|
||||
fullNameLabel.setAlignmentX( LEFT_ALIGNMENT );
|
||||
fullNameLabel.setHorizontalAlignment( SwingConstants.CENTER );
|
||||
fullNameLabel.setVerticalAlignment( SwingConstants.BOTTOM );
|
||||
add( fullNameLabel );
|
||||
|
||||
userNameField = new JTextField() {
|
||||
fullNameField = new JTextField() {
|
||||
@Override
|
||||
public Dimension getMaximumSize() {
|
||||
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
|
||||
}
|
||||
};
|
||||
userNameField.setAlignmentX( Component.LEFT_ALIGNMENT );
|
||||
userNameField.getDocument().addDocumentListener( this );
|
||||
userNameField.addActionListener( this );
|
||||
add( userNameField );
|
||||
fullNameField.setAlignmentX( LEFT_ALIGNMENT );
|
||||
fullNameField.getDocument().addDocumentListener( this );
|
||||
fullNameField.addActionListener( this );
|
||||
add( fullNameField );
|
||||
|
||||
// Master Password
|
||||
JLabel masterPasswordLabel = new JLabel( "Master Password:" );
|
||||
masterPasswordLabel.setAlignmentX( Component.LEFT_ALIGNMENT );
|
||||
masterPasswordLabel.setAlignmentX( LEFT_ALIGNMENT );
|
||||
masterPasswordLabel.setHorizontalAlignment( SwingConstants.CENTER );
|
||||
masterPasswordLabel.setVerticalAlignment( SwingConstants.BOTTOM );
|
||||
add( masterPasswordLabel );
|
||||
@ -50,7 +50,7 @@ public class TextAuthenticationPanel extends AuthenticationPanel implements Docu
|
||||
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
|
||||
}
|
||||
};
|
||||
masterPasswordField.setAlignmentX( Component.LEFT_ALIGNMENT );
|
||||
masterPasswordField.setAlignmentX( LEFT_ALIGNMENT );
|
||||
masterPasswordField.addActionListener( this );
|
||||
masterPasswordField.getDocument().addDocumentListener( this );
|
||||
add( masterPasswordField );
|
||||
@ -58,12 +58,12 @@ public class TextAuthenticationPanel extends AuthenticationPanel implements Docu
|
||||
|
||||
@Override
|
||||
public Component getFocusComponent() {
|
||||
return userNameField;
|
||||
return fullNameField;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected User getUser() {
|
||||
return new User( userNameField.getText(), new String( masterPasswordField.getPassword() ) );
|
||||
return new IncognitoUser( fullNameField.getText(), new String( masterPasswordField.getPassword() ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -84,6 +84,6 @@ public class TextAuthenticationPanel extends AuthenticationPanel implements Docu
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
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 com.google.common.collect.Iterables;
|
||||
import com.lyndir.masterpassword.*;
|
||||
import com.lyndir.masterpassword.util.Components;
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
@ -38,7 +39,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
||||
} );
|
||||
|
||||
// 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.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.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() {
|
||||
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.*;
|
||||
|
||||
@ -18,7 +18,7 @@ public class UnlockFrame extends JFrame {
|
||||
private final JPanel root;
|
||||
private final JButton signInButton;
|
||||
private final JPanel authenticationContainer;
|
||||
private boolean useConfig;
|
||||
private boolean incognito;
|
||||
public User user;
|
||||
|
||||
public UnlockFrame(final SignInCallback signInCallback)
|
||||
@ -46,7 +46,6 @@ public class UnlockFrame extends JFrame {
|
||||
}
|
||||
} );
|
||||
|
||||
useConfig = ConfigAuthenticationPanel.hasConfigUsers();
|
||||
createAuthenticationPanel();
|
||||
|
||||
setLocationByPlatform( true );
|
||||
@ -65,21 +64,21 @@ public class UnlockFrame extends JFrame {
|
||||
authenticationContainer.removeAll();
|
||||
|
||||
final AuthenticationPanel authenticationPanel;
|
||||
if (useConfig) {
|
||||
authenticationPanel = new ConfigAuthenticationPanel( this );
|
||||
if (incognito) {
|
||||
authenticationPanel = new IncognitoAuthenticationPanel( this );
|
||||
} else {
|
||||
authenticationPanel = new TextAuthenticationPanel( this );
|
||||
authenticationPanel = new ModelAuthenticationPanel( this );
|
||||
}
|
||||
authenticationPanel.updateUser( false );
|
||||
authenticationContainer.add( authenticationPanel, BorderLayout.CENTER );
|
||||
|
||||
final JCheckBox typeCheckBox = new JCheckBox( "Use Config File" );
|
||||
typeCheckBox.setAlignmentX( LEFT_ALIGNMENT );
|
||||
typeCheckBox.setSelected( useConfig );
|
||||
typeCheckBox.addItemListener( new ItemListener() {
|
||||
final JCheckBox incognitoCheckBox = new JCheckBox( "Incognito" );
|
||||
incognitoCheckBox.setAlignmentX( LEFT_ALIGNMENT );
|
||||
incognitoCheckBox.setSelected( incognito );
|
||||
incognitoCheckBox.addItemListener( new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(final ItemEvent e) {
|
||||
useConfig = typeCheckBox.isSelected();
|
||||
incognito = incognitoCheckBox.isSelected();
|
||||
SwingUtilities.invokeLater( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@ -89,24 +88,15 @@ public class UnlockFrame extends JFrame {
|
||||
}
|
||||
} );
|
||||
|
||||
JButton typeHelp = new JButton( Res.iconQuestion() );
|
||||
typeHelp.setMargin( new Insets( 0, 0, 0, 0 ) );
|
||||
typeHelp.setBackground( Color.red );
|
||||
typeHelp.setAlignmentX( RIGHT_ALIGNMENT );
|
||||
typeHelp.setBorder( null );
|
||||
typeHelp.addActionListener( new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
JOptionPane.showMessageDialog( UnlockFrame.this, authenticationPanel.getHelpText(), "Help",
|
||||
JOptionPane.INFORMATION_MESSAGE );
|
||||
}
|
||||
} );
|
||||
if (authenticationPanel.getHelpText() == null) {
|
||||
typeHelp.setVisible( false );
|
||||
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 ) );
|
||||
button.setAlignmentX( RIGHT_ALIGNMENT );
|
||||
button.setBorder( null );
|
||||
toolsPanel.add( button );
|
||||
}
|
||||
JComponent typePanel = Components.boxLayout( BoxLayout.LINE_AXIS, typeCheckBox, Box.createGlue(), typeHelp );
|
||||
typePanel.setAlignmentX( Component.LEFT_ALIGNMENT );
|
||||
authenticationContainer.add( typePanel );
|
||||
|
||||
checkSignIn();
|
||||
validate();
|
||||
@ -126,7 +116,7 @@ public class UnlockFrame extends JFrame {
|
||||
}
|
||||
|
||||
boolean checkSignIn() {
|
||||
boolean enabled = user != null && !user.getUserName().isEmpty() && user.hasKey();
|
||||
boolean enabled = user != null && !user.getFullName().isEmpty() && user.hasKey();
|
||||
signInButton.setEnabled( 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 DateTimeFormatter rfc3339 = ISODateTimeFormat.dateTime();
|
||||
private static final Pattern[] unmarshallFormats = new Pattern[]{
|
||||
Pattern.compile( "^([^ ]+) +([[:digit:]]+) +([[:digit:]]+)(:[[:digit:]]+)? +([^\t]+)\t(.*)" ),
|
||||
Pattern.compile( "^([^ ]+) +([[:digit:]]+) +([[:digit:]]+)(:[[:digit:]]+)?(:[[:digit:]]+)? +([^\t]*)\t *([^\t]+)\t(.*)" ) };
|
||||
private static final Pattern headerFormat = Pattern.compile( "^#[[:space:]]*([^:]+): (.*)" );
|
||||
Pattern.compile( "^([^ ]+) +(\\d+) +(\\d+)(:\\d+)? +([^\t]+)\t(.*)" ),
|
||||
Pattern.compile( "^([^ ]+) +(\\d+) +(\\d+)(:\\d+)?(:\\d+)? +([^\t]*)\t *([^\t]+)\t(.*)" ) };
|
||||
private static final Pattern headerFormat = Pattern.compile( "^#\\s*([^:]+): (.*)" );
|
||||
|
||||
private final int importFormat;
|
||||
private final int mpVersion;
|
||||
|
@ -1,10 +1,11 @@
|
||||
package com.lyndir.masterpassword.model;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||
import com.lyndir.masterpassword.MPSiteType;
|
||||
import java.util.*;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.*;
|
||||
|
||||
|
||||
/**
|
||||
@ -12,18 +13,24 @@ import org.joda.time.DateTime;
|
||||
*/
|
||||
public class MPUser {
|
||||
|
||||
private final String fullName;
|
||||
private final byte[] keyID;
|
||||
private final int avatar;
|
||||
private final MPSiteType defaultType;
|
||||
private final DateTime lastUsed;
|
||||
private final String fullName;
|
||||
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) {
|
||||
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.keyID = keyID;
|
||||
this.avatar = avatar;
|
||||
@ -31,6 +38,15 @@ public class MPUser {
|
||||
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) {
|
||||
sites.add( site );
|
||||
}
|
||||
@ -39,6 +55,10 @@ public class MPUser {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public boolean hasKeyID() {
|
||||
return keyID != null;
|
||||
}
|
||||
|
||||
public boolean hasKeyID(final byte[] keyID) {
|
||||
return Arrays.equals( this.keyID, keyID );
|
||||
}
|
||||
@ -47,18 +67,34 @@ public class MPUser {
|
||||
return CodeUtils.encodeHex( keyID );
|
||||
}
|
||||
|
||||
public void setKeyID(final byte[] keyID) {
|
||||
this.keyID = keyID;
|
||||
}
|
||||
|
||||
public int getAvatar() {
|
||||
return avatar;
|
||||
}
|
||||
|
||||
public void setAvatar(final int avatar) {
|
||||
this.avatar = avatar;
|
||||
}
|
||||
|
||||
public MPSiteType getDefaultType() {
|
||||
return defaultType;
|
||||
}
|
||||
|
||||
public DateTime getLastUsed() {
|
||||
public void setDefaultType(final MPSiteType defaultType) {
|
||||
this.defaultType = defaultType;
|
||||
}
|
||||
|
||||
public ReadableInstant getLastUsed() {
|
||||
return lastUsed;
|
||||
}
|
||||
|
||||
public void updateLastUsed() {
|
||||
this.lastUsed = new Instant();
|
||||
}
|
||||
|
||||
public Iterable<MPSite> getSites() {
|
||||
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