2
0

Adding and deleting users and sites.

This commit is contained in:
Maarten Billemont 2018-07-28 17:52:43 -04:00
parent 8cd9755616
commit 7455fba55e
17 changed files with 285 additions and 141 deletions

View File

@ -83,6 +83,10 @@ public abstract class Components {
return box; return box;
} }
public static GradientPanel panel(@Nullable final LayoutManager layout) {
return panel( layout, null );
}
public static GradientPanel panel(@Nullable final LayoutManager layout, @Nullable final Color color) { public static GradientPanel panel(@Nullable final LayoutManager layout, @Nullable final Color color) {
return new GradientPanel( layout, color ); return new GradientPanel( layout, color );
} }
@ -223,6 +227,11 @@ public abstract class Components {
if (actionListener != null) if (actionListener != null)
actionListener.actionPerformed( e ); actionListener.actionPerformed( e );
} }
@Override
public boolean isEnabled() {
return actionListener != null;
}
} ); } );
} }
@ -233,6 +242,11 @@ public abstract class Components {
if (actionListener != null) if (actionListener != null)
actionListener.actionPerformed( e ); actionListener.actionPerformed( e );
} }
@Override
public boolean isEnabled() {
return actionListener != null;
}
} ); } );
iconButton.setFocusable( false ); iconButton.setFocusable( false );
@ -441,6 +455,9 @@ public abstract class Components {
public GradientPanel(@Nullable final LayoutManager layout, @Nullable final Color gradientColor) { public GradientPanel(@Nullable final LayoutManager layout, @Nullable final Color gradientColor) {
super( layout ); super( layout );
if (getLayout() == null)
setLayout( new BoxLayout( this, BoxLayout.PAGE_AXIS ) );
setGradientColor( gradientColor ); setGradientColor( gradientColor );
setBackground( null ); setBackground( null );
setAlignmentX( LEFT_ALIGNMENT ); setAlignmentX( LEFT_ALIGNMENT );
@ -459,7 +476,7 @@ public abstract class Components {
@Override @Override
public void setBackground(@Nullable final Color bg) { public void setBackground(@Nullable final Color bg) {
super.setBackground( bg ); super.setBackground( bg );
updatePaint(); setOpaque( bg != null );
} }
@Override @Override
@ -469,8 +486,6 @@ public abstract class Components {
} }
private void updatePaint() { private void updatePaint() {
setOpaque( (getGradientColor() != null) || (getBackground() != null) );
if (gradientColor == null) { if (gradientColor == null) {
paint = null; paint = null;
return; return;

View File

@ -128,6 +128,10 @@ public abstract class Res {
return icon( "media/icon_user.png" ); return icon( "media/icon_user.png" );
} }
public Icon lock() {
return icon( "media/icon_lock.png" );
}
public Icon settings() { public Icon settings() {
return icon( "media/icon_settings.png" ); return icon( "media/icon_settings.png" );
} }

View File

@ -2,10 +2,12 @@ package com.lyndir.masterpassword.gui.view;
import static com.lyndir.masterpassword.util.Utilities.*; import static com.lyndir.masterpassword.util.Utilities.*;
import com.google.common.collect.ImmutableSortedSet;
import com.lyndir.masterpassword.gui.util.Res; import com.lyndir.masterpassword.gui.util.Res;
import com.lyndir.masterpassword.gui.util.CollectionListModel; import com.lyndir.masterpassword.gui.util.CollectionListModel;
import com.lyndir.masterpassword.gui.util.Components; import com.lyndir.masterpassword.gui.util.Components;
import com.lyndir.masterpassword.model.MPUser; import com.lyndir.masterpassword.model.MPUser;
import com.lyndir.masterpassword.model.impl.MPFileUser;
import com.lyndir.masterpassword.model.impl.MPFileUserManager; import com.lyndir.masterpassword.model.impl.MPFileUserManager;
import java.awt.*; import java.awt.*;
import java.util.Collection; import java.util.Collection;
@ -18,7 +20,7 @@ import javax.swing.*;
* @author lhunath, 2018-07-14 * @author lhunath, 2018-07-14
*/ */
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class FilesPanel extends JPanel { public class FilesPanel extends JPanel implements MPFileUserManager.Listener {
private final Collection<Listener> listeners = new CopyOnWriteArraySet<>(); private final Collection<Listener> listeners = new CopyOnWriteArraySet<>();
@ -48,11 +50,8 @@ public class FilesPanel extends JPanel {
// User Selection // User Selection
add( userField ); add( userField );
}
public void reload() { MPFileUserManager.get().addListener( this );
// TODO: Should we use a listener here instead?
usersModel.set( MPFileUserManager.get().reload() );
} }
public boolean addListener(final Listener listener) { public boolean addListener(final Listener listener) {
@ -75,6 +74,11 @@ public class FilesPanel extends JPanel {
listener.onUserSelected( user ); listener.onUserSelected( user );
} }
@Override
public void onFilesUpdated(final ImmutableSortedSet<MPFileUser> files) {
usersModel.set( files );
}
public interface Listener { public interface Listener {
void onUserSelected(@Nullable MPUser<?> user); void onUserSelected(@Nullable MPUser<?> user);

View File

@ -1,13 +1,12 @@
package com.lyndir.masterpassword.gui.view; package com.lyndir.masterpassword.gui.view;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.gui.util.Res;
import com.lyndir.masterpassword.gui.util.Components; import com.lyndir.masterpassword.gui.util.Components;
import com.lyndir.masterpassword.model.MPUser; import com.lyndir.masterpassword.gui.util.Res;
import com.lyndir.masterpassword.model.impl.MPFileUserManager;
import java.awt.*; import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent; import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import javax.annotation.Nullable;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.BevelBorder; import javax.swing.border.BevelBorder;
@ -16,57 +15,48 @@ import javax.swing.border.BevelBorder;
* @author lhunath, 2018-07-14 * @author lhunath, 2018-07-14
*/ */
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class MasterPasswordFrame extends JFrame implements FilesPanel.Listener, ComponentListener { public class MasterPasswordFrame extends JFrame {
private static final Logger logger = Logger.get( MasterPasswordFrame.class ); private static final Logger logger = Logger.get( MasterPasswordFrame.class );
@SuppressWarnings("FieldCanBeLocal") @SuppressWarnings("FieldCanBeLocal")
private final Components.GradientPanel root; private final Components.GradientPanel root = Components.borderPanel( Res.colors().frameBg(), BoxLayout.PAGE_AXIS );
private final FilesPanel filesPanel = new FilesPanel(); private final FilesPanel filesPanel = new FilesPanel();
private final UserPanel userPanel = new UserPanel(); private final JPanel userPanel = Components.panel( new BorderLayout( 0, 0 ) );
private final UserContentPanel userContent = new UserContentPanel();
@SuppressWarnings("MagicNumber") @SuppressWarnings("MagicNumber")
public MasterPasswordFrame() { public MasterPasswordFrame() {
super( "Master Password" ); super( "Master Password" );
setDefaultCloseOperation( DISPOSE_ON_CLOSE ); setContentPane( root );
setContentPane( root = Components.borderPanel( Res.colors().frameBg(), BoxLayout.PAGE_AXIS ) );
root.add( filesPanel ); root.add( filesPanel );
root.add( Components.strut() ); root.add( Components.strut() );
root.add( Components.borderPanel( root.add( userPanel );
userPanel.add( userContent.getUserToolbar(), BorderLayout.LINE_START );
userPanel.add( userContent.getSiteToolbar(), BorderLayout.LINE_END );
userPanel.add( Components.borderPanel(
BorderFactory.createBevelBorder( BevelBorder.RAISED, Res.colors().controlBorder(), Res.colors().frameBg() ), BorderFactory.createBevelBorder( BevelBorder.RAISED, Res.colors().controlBorder(), Res.colors().frameBg() ),
Res.colors().controlBg(), BoxLayout.PAGE_AXIS, userPanel ) ); Res.colors().controlBg(), BoxLayout.PAGE_AXIS, userContent ), BorderLayout.CENTER );
filesPanel.addListener( this ); filesPanel.addListener( userContent );
filesPanel.reload();
addComponentListener( this ); addComponentListener( new ComponentHandler() );
setPreferredSize( new Dimension( 640, 480 ) ); setPreferredSize( new Dimension( 800, 560 ) );
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
pack(); pack();
setLocationRelativeTo( null ); setLocationRelativeTo( null );
setLocationByPlatform( true ); setLocationByPlatform( true );
} }
@Override private class ComponentHandler extends ComponentAdapter {
public void onUserSelected(@Nullable final MPUser<?> user) {
userPanel.setUser( user );
}
@Override
public void componentResized(final ComponentEvent e) {
}
@Override
public void componentMoved(final ComponentEvent e) {
}
@Override @Override
public void componentShown(final ComponentEvent e) { public void componentShown(final ComponentEvent e) {
userPanel.transferFocus(); MPFileUserManager.get().reload();
userContent.transferFocus();
} }
@Override
public void componentHidden(final ComponentEvent e) {
} }
} }

View File

@ -11,8 +11,7 @@ import com.lyndir.masterpassword.*;
import com.lyndir.masterpassword.gui.model.MPNewSite; import com.lyndir.masterpassword.gui.model.MPNewSite;
import com.lyndir.masterpassword.gui.util.*; import com.lyndir.masterpassword.gui.util.*;
import com.lyndir.masterpassword.model.*; import com.lyndir.masterpassword.model.*;
import com.lyndir.masterpassword.model.impl.MPFileSite; import com.lyndir.masterpassword.model.impl.*;
import com.lyndir.masterpassword.model.impl.MPFileUser;
import java.awt.*; import java.awt.*;
import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable; import java.awt.datatransfer.Transferable;
@ -32,44 +31,38 @@ import javax.swing.event.DocumentListener;
* @author lhunath, 2018-07-14 * @author lhunath, 2018-07-14
*/ */
@SuppressWarnings("SerializableStoresNonSerializable") @SuppressWarnings("SerializableStoresNonSerializable")
public class UserPanel extends Components.GradientPanel implements MPUser.Listener { public class UserContentPanel extends JPanel implements FilesPanel.Listener, MPUser.Listener {
private static final Logger logger = Logger.get( UserPanel.class ); private static final Random random = new Random();
private static final Logger logger = Logger.get( UserContentPanel.class );
private static final JButton iconButton = Components.button( Res.icons().user(), null );
private final JPanel userToolbar = Components.panel( BoxLayout.PAGE_AXIS );
private final JPanel siteToolbar = Components.panel( BoxLayout.PAGE_AXIS );
@Nullable @Nullable
private MPUser<?> user; private MPUser<?> listeningUser;
public UserPanel() { public UserContentPanel() {
super( new BorderLayout( Components.margin(), Components.margin() ), null ); userToolbar.setPreferredSize( iconButton.getPreferredSize() );
siteToolbar.setPreferredSize( iconButton.getPreferredSize() );
setLayout( new BoxLayout( this, BoxLayout.PAGE_AXIS ) );
setBorder( Components.marginBorder() ); setBorder( Components.marginBorder() );
setUser( null ); setUser( null );
} }
public void setUser(@Nullable final MPUser<?> user) { protected JComponent getUserToolbar() {
if ((this.user != null) && !Objects.equals( this.user, user )) return userToolbar;
this.user.removeListener( this );
this.user = user;
if (this.user != null)
this.user.addListener( this );
Res.ui( () -> {
removeAll();
if (this.user == null)
add( new NoUserPanel(), BorderLayout.CENTER );
else {
if (!this.user.isMasterKeyAvailable())
add( new AuthenticateUserPanel( this.user ), BorderLayout.CENTER );
else
add( new AuthenticatedUserPanel( this.user ), BorderLayout.CENTER );
} }
revalidate(); protected JComponent getSiteToolbar() {
transferFocus(); return siteToolbar;
} ); }
@Override
public void onUserSelected(@Nullable final MPUser<?> user) {
setUser( user );
} }
@Override @Override
@ -82,7 +75,40 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
setUser( user ); setUser( user );
} }
private static final class NoUserPanel extends JPanel { @Override
public void onUserInvalidated(final MPUser<?> user) {
setUser( user );
}
private void setUser(@Nullable final MPUser<?> user) {
Res.ui( () -> {
if (listeningUser != null)
listeningUser.removeListener( this );
listeningUser = user;
userToolbar.removeAll();
siteToolbar.removeAll();
removeAll();
if (user == null)
add( new NoUserPanel() );
else {
user.addListener( this );
if (!user.isMasterKeyAvailable())
add( new AuthenticateUserPanel( user ) );
else
add( new AuthenticatedUserPanel( user ) );
}
revalidate();
transferFocus();
} );
}
private final class NoUserPanel extends JPanel {
private NoUserPanel() { private NoUserPanel() {
setLayout( new BoxLayout( this, BoxLayout.PAGE_AXIS ) ); setLayout( new BoxLayout( this, BoxLayout.PAGE_AXIS ) );
@ -94,13 +120,14 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
} }
private static final class AuthenticateUserPanel extends JPanel implements ActionListener, DocumentListener { private final class AuthenticateUserPanel extends JPanel implements ActionListener, DocumentListener {
private static final Random random = new Random();
@Nonnull @Nonnull
private final MPUser<?> user; private final MPUser<?> user;
private final JButton addButton = Components.button( Res.icons().add(), event -> addUser() );
private final JButton deleteButton = Components.button( Res.icons().delete(), event -> deleteUser() );
private final JPasswordField masterPasswordField = Components.passwordField(); private final JPasswordField masterPasswordField = Components.passwordField();
private final JLabel errorLabel = Components.label(); private final JLabel errorLabel = Components.label();
private final JLabel identiconLabel = Components.label( SwingConstants.CENTER ); private final JLabel identiconLabel = Components.label( SwingConstants.CENTER );
@ -112,7 +139,14 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
this.user = user; this.user = user;
userToolbar.add( addButton );
userToolbar.add( deleteButton );
add( Components.heading( user.getFullName(), SwingConstants.CENTER ) ); add( Components.heading( user.getFullName(), SwingConstants.CENTER ) );
add( Components.strut() );
add( identiconLabel );
identiconLabel.setFont( Res.fonts().emoticonsFont( Components.TEXT_SIZE_CONTROL ) );
add( Box.createGlue() ); add( Box.createGlue() );
add( Components.label( "Master Password:" ) ); add( Components.label( "Master Password:" ) );
@ -122,14 +156,31 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
masterPasswordField.getDocument().addDocumentListener( this ); masterPasswordField.getDocument().addDocumentListener( this );
add( errorLabel ); add( errorLabel );
errorLabel.setForeground( Res.colors().errorFg() ); errorLabel.setForeground( Res.colors().errorFg() );
add( Components.strut() );
add( identiconLabel );
identiconLabel.setFont( Res.fonts().emoticonsFont( Components.TEXT_SIZE_CONTROL ) );
add( Box.createGlue() ); add( Box.createGlue() );
} }
private void addUser() {
Object fullName = JOptionPane.showInputDialog(
this, strf( "<html>Enter your full legal name:</html>" ), "Add User",
JOptionPane.QUESTION_MESSAGE, null, null, "Robert Lee Mitchell" );
if (fullName == null)
return;
setUser( MPFileUserManager.get().add( fullName.toString() ) );
}
private void deleteUser() {
MPFileUser fileUser = (user instanceof MPFileUser)? (MPFileUser) user: null;
if (fileUser == null)
return;
if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(
this, strf( "<html>Delete the user <strong>%s</strong>?<br><br><em>%s</em></html>",
fileUser.getFullName(), fileUser.getFile().getName() ),
"Delete User", JOptionPane.YES_NO_OPTION ))
MPFileUserManager.get().delete( fileUser );
}
@Override @Override
public void actionPerformed(final ActionEvent event) { public void actionPerformed(final ActionEvent event) {
updateIdenticon(); updateIdenticon();
@ -193,16 +244,20 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
} }
private static final class AuthenticatedUserPanel extends JPanel implements KeyListener { private final class AuthenticatedUserPanel extends JPanel implements KeyListener {
public static final int SIZE_RESULT = 48; public static final int SIZE_RESULT = 48;
private final JButton userButton = Components.button( Res.icons().user(), event -> showUserPreferences() );
private final JButton logoutButton = Components.button( Res.icons().lock(), event -> logoutUser() );
private final JButton settingsButton = Components.button( Res.icons().settings(), event -> showSiteSettings() );
private final JButton questionsButton = Components.button( Res.icons().question(), null );
private final JButton deleteButton = Components.button( Res.icons().delete(), event -> deleteSite() );
@Nonnull @Nonnull
private final MPUser<?> user; private final MPUser<?> user;
private final JLabel passwordLabel = Components.label( SwingConstants.CENTER ); private final JLabel passwordLabel = Components.label( SwingConstants.CENTER );
private final JLabel passwordField = Components.heading( SwingConstants.CENTER ); private final JLabel passwordField = Components.heading( SwingConstants.CENTER );
private final JButton passwordButton =
Components.button( Res.icons().settings(), event -> showSiteSettings() );
private final JLabel queryLabel = Components.label(); private final JLabel queryLabel = Components.label();
private final JTextField queryField = Components.textField( null, this::updateSites ); private final JTextField queryField = Components.textField( null, this::updateSites );
private final CollectionListModel<MPSite<?>> sitesModel = private final CollectionListModel<MPSite<?>> sitesModel =
@ -217,23 +272,20 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
this.user = user; this.user = user;
add( Components.panel( userToolbar.add( userButton );
Components.heading( user.getFullName(), SwingConstants.CENTER ), userToolbar.add( logoutButton );
Components.panel(
BoxLayout.LINE_AXIS, siteToolbar.add( settingsButton );
Box.createGlue(), siteToolbar.add( questionsButton );
Components.button( Res.icons().user(), event -> showUserPreferences() ) ) ) ); siteToolbar.add( deleteButton );
settingsButton.setEnabled( false );
add( Components.heading( user.getFullName(), SwingConstants.CENTER ) );
add( passwordLabel ); add( passwordLabel );
add( Components.panel( add( passwordField );
passwordField,
Components.panel(
BoxLayout.LINE_AXIS,
Box.createGlue(),
passwordButton ) ) );
passwordField.setForeground( Res.colors().highlightFg() ); passwordField.setForeground( Res.colors().highlightFg() );
passwordField.setFont( Res.fonts().bigValueFont( SIZE_RESULT ) ); passwordField.setFont( Res.fonts().bigValueFont( SIZE_RESULT ) );
passwordButton.setVisible( false );
add( Box.createGlue() ); add( Box.createGlue() );
add( Components.strut() ); add( Components.strut() );
@ -269,13 +321,16 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
BoxLayout.PAGE_AXIS, components.build().toArray( new Component[0] ) ) ) ); BoxLayout.PAGE_AXIS, components.build().toArray( new Component[0] ) ) ) );
} }
public void showSiteSettings() { public void logoutUser() {
ImmutableList.Builder<Component> components = ImmutableList.builder(); user.invalidate();
}
public void showSiteSettings() {
MPSite<?> site = sitesModel.getSelectedItem(); MPSite<?> site = sitesModel.getSelectedItem();
if (site == null) if (site == null)
return; return;
ImmutableList.Builder<Component> components = ImmutableList.builder();
components.add( Components.label( "Algorithm:" ), components.add( Components.label( "Algorithm:" ),
Components.comboBox( MPAlgorithm.Version.values(), MPAlgorithm.Version::name, Components.comboBox( MPAlgorithm.Version.values(), MPAlgorithm.Version::name,
site.getAlgorithm().version(), site.getAlgorithm().version(),
@ -306,6 +361,14 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
BoxLayout.PAGE_AXIS, components.build().toArray( new Component[0] ) ) ) ); BoxLayout.PAGE_AXIS, components.build().toArray( new Component[0] ) ) ) );
} }
public void deleteSite() {
MPSite<?> site = sitesModel.getSelectedItem();
if (site == null)
return;
user.deleteSite( site );
}
private String getSiteDescription(@Nonnull final MPSite<?> site) { private String getSiteDescription(@Nonnull final MPSite<?> site) {
if (site instanceof MPNewSite) if (site instanceof MPNewSite)
return strf( "<html><strong>%s</strong> &lt;Add new site&gt;</html>", queryField.getText() ); return strf( "<html><strong>%s</strong> &lt;Add new site&gt;</html>", queryField.getText() );
@ -334,7 +397,7 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
MPSite<?> site = sitesModel.getSelectedItem(); MPSite<?> site = sitesModel.getSelectedItem();
if (site instanceof MPNewSite) { if (site instanceof MPNewSite) {
if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog( if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(
this, strf( "<html>Remember the site [<strong>%s</strong>]?</html>", site.getSiteName() ), this, strf( "<html>Remember the site <strong>%s</strong>?</html>", site.getSiteName() ),
"New Site", JOptionPane.YES_NO_OPTION )) { "New Site", JOptionPane.YES_NO_OPTION )) {
sitesModel.setSelectedItem( user.addSite( site.getSiteName() ) ); sitesModel.setSelectedItem( user.addSite( site.getSiteName() ) );
useSite(); useSite();
@ -370,7 +433,7 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
Res.ui( () -> { Res.ui( () -> {
passwordLabel.setText( " " ); passwordLabel.setText( " " );
passwordField.setText( " " ); passwordField.setText( " " );
passwordButton.setVisible( false ); settingsButton.setEnabled( false );
} ); } );
return; return;
} }
@ -384,7 +447,7 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
Res.ui( () -> { Res.ui( () -> {
passwordLabel.setText( strf( "Your password for %s:", site.getSiteName() ) ); passwordLabel.setText( strf( "Your password for %s:", site.getSiteName() ) );
passwordField.setText( result ); passwordField.setText( result );
passwordButton.setVisible( true ); settingsButton.setEnabled( true );
} ); } );
} }
catch (final MPKeyUnavailableException | MPAlgorithmException e) { catch (final MPKeyUnavailableException | MPAlgorithmException e) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -94,9 +94,9 @@ public interface MPSite<Q extends MPQuestion> extends Comparable<MPSite<?>> {
@Nonnull @Nonnull
MPUser<?> getUser(); MPUser<?> getUser();
void addQuestion(Q question); boolean addQuestion(Q question);
void deleteQuestion(Q question); boolean deleteQuestion(Q question);
@Nonnull @Nonnull
Collection<Q> getQuestions(); Collection<Q> getQuestions();

View File

@ -77,6 +77,8 @@ public interface MPUser<S extends MPSite<?>> extends Comparable<MPUser<?>> {
void authenticate(MPMasterKey masterKey) void authenticate(MPMasterKey masterKey)
throws MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException; throws MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException;
void invalidate();
boolean isMasterKeyAvailable(); boolean isMasterKeyAvailable();
@Nonnull @Nonnull
@ -90,7 +92,7 @@ public interface MPUser<S extends MPSite<?>> extends Comparable<MPUser<?>> {
@Nonnull @Nonnull
S addSite(S site); S addSite(S site);
void deleteSite(S site); boolean deleteSite(MPSite<?> site);
@Nonnull @Nonnull
Collection<S> getSites(); Collection<S> getSites();
@ -107,5 +109,7 @@ public interface MPUser<S extends MPSite<?>> extends Comparable<MPUser<?>> {
void onUserUpdated(MPUser<?> user); void onUserUpdated(MPUser<?> user);
void onUserAuthenticated(MPUser<?> user); void onUserAuthenticated(MPUser<?> user);
void onUserInvalidated(MPUser<?> user);
} }
} }

View File

@ -55,8 +55,10 @@ public abstract class MPBasicQuestion extends Changeable implements MPQuestion {
@Override @Override
public void setType(final MPResultType type) { public void setType(final MPResultType type) {
this.type = type; if (Objects.equals(this.type, type))
return;
this.type = type;
setChanged(); setChanged();
} }

View File

@ -73,8 +73,10 @@ public abstract class MPBasicSite<U extends MPUser<?>, Q extends MPQuestion> ext
@Override @Override
public void setAlgorithm(final MPAlgorithm algorithm) { public void setAlgorithm(final MPAlgorithm algorithm) {
this.algorithm = algorithm; if (Objects.equals(this.algorithm, algorithm))
return;
this.algorithm = algorithm;
setChanged(); setChanged();
} }
@ -86,8 +88,10 @@ public abstract class MPBasicSite<U extends MPUser<?>, Q extends MPQuestion> ext
@Override @Override
public void setCounter(final UnsignedInteger counter) { public void setCounter(final UnsignedInteger counter) {
this.counter = counter; if (Objects.equals(this.counter, counter))
return;
this.counter = counter;
setChanged(); setChanged();
} }
@ -99,8 +103,10 @@ public abstract class MPBasicSite<U extends MPUser<?>, Q extends MPQuestion> ext
@Override @Override
public void setResultType(final MPResultType resultType) { public void setResultType(final MPResultType resultType) {
this.resultType = resultType; if (Objects.equals(this.resultType, resultType))
return;
this.resultType = resultType;
setChanged(); setChanged();
} }
@ -112,8 +118,10 @@ public abstract class MPBasicSite<U extends MPUser<?>, Q extends MPQuestion> ext
@Override @Override
public void setLoginType(@Nullable final MPResultType loginType) { public void setLoginType(@Nullable final MPResultType loginType) {
this.loginType = ifNotNullElse( loginType, getAlgorithm().mpw_default_login_type() ); if (Objects.equals(this.loginType, loginType))
return;
this.loginType = ifNotNullElse( loginType, getAlgorithm().mpw_default_login_type() );
setChanged(); setChanged();
} }
@ -152,17 +160,21 @@ public abstract class MPBasicSite<U extends MPUser<?>, Q extends MPQuestion> ext
} }
@Override @Override
public void addQuestion(final Q question) { public boolean addQuestion(final Q question) {
questions.add( question ); if (!questions.add( question ))
return false;
setChanged(); setChanged();
return true;
} }
@Override @Override
public void deleteQuestion(final Q question) { public boolean deleteQuestion(final Q question) {
questions.remove( question ); if (!questions.remove( question ))
return false;
setChanged(); setChanged();
return true;
} }
@Nonnull @Nonnull

View File

@ -25,8 +25,7 @@ import com.google.common.collect.ImmutableSortedSet;
import com.lyndir.lhunath.opal.system.CodeUtils; import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.*; import com.lyndir.masterpassword.*;
import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException; import com.lyndir.masterpassword.model.*;
import com.lyndir.masterpassword.model.MPUser;
import java.util.*; import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -66,8 +65,10 @@ public abstract class MPBasicUser<S extends MPBasicSite<?, ?>> extends Changeabl
@Override @Override
public void setAvatar(final int avatar) { public void setAvatar(final int avatar) {
this.avatar = avatar; if (Objects.equals(this.avatar, avatar))
return;
this.avatar = avatar;
setChanged(); setChanged();
} }
@ -85,8 +86,10 @@ public abstract class MPBasicUser<S extends MPBasicSite<?, ?>> extends Changeabl
@Override @Override
public void setAlgorithm(final MPAlgorithm algorithm) { public void setAlgorithm(final MPAlgorithm algorithm) {
this.algorithm = algorithm; if (Objects.equals(this.algorithm, algorithm))
return;
this.algorithm = algorithm;
setChanged(); setChanged();
} }
@ -136,6 +139,17 @@ public abstract class MPBasicUser<S extends MPBasicSite<?, ?>> extends Changeabl
listener.onUserAuthenticated( this ); listener.onUserAuthenticated( this );
} }
@Override
public void invalidate() {
if (masterKey == null)
return;
this.masterKey = null;
for (final Listener listener : listeners)
listener.onUserInvalidated( this );
}
@Override @Override
public boolean isMasterKeyAvailable() { public boolean isMasterKeyAvailable() {
return masterKey != null; return masterKey != null;
@ -151,6 +165,7 @@ public abstract class MPBasicUser<S extends MPBasicSite<?, ?>> extends Changeabl
return masterKey; return masterKey;
} }
@Nonnull
@Override @Override
public S addSite(final S site) { public S addSite(final S site) {
sites.put( site.getSiteName(), site ); sites.put( site.getSiteName(), site );
@ -160,10 +175,12 @@ public abstract class MPBasicUser<S extends MPBasicSite<?, ?>> extends Changeabl
} }
@Override @Override
public void deleteSite(final S site) { public boolean deleteSite(final MPSite<?> site) {
sites.values().remove( site ); if (!sites.values().remove( site ))
return false;
setChanged(); setChanged();
return true;
} }
@Nonnull @Nonnull

View File

@ -21,6 +21,7 @@ package com.lyndir.masterpassword.model.impl;
import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedInteger;
import com.lyndir.masterpassword.*; import com.lyndir.masterpassword.*;
import com.lyndir.masterpassword.model.MPSite; import com.lyndir.masterpassword.model.MPSite;
import java.util.Objects;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.joda.time.Instant; import org.joda.time.Instant;
@ -75,8 +76,10 @@ public class MPFileSite extends MPBasicSite<MPFileUser, MPFileQuestion> {
} }
public void setUrl(@Nullable final String url) { public void setUrl(@Nullable final String url) {
this.url = url; if (Objects.equals( this.url, url))
return;
this.url = url;
setChanged(); setChanged();
} }
@ -92,7 +95,6 @@ public class MPFileSite extends MPBasicSite<MPFileUser, MPFileQuestion> {
uses++; uses++;
lastUsed = new Instant(); lastUsed = new Instant();
getUser().use(); getUser().use();
setChanged(); setChanged();
} }

View File

@ -23,6 +23,7 @@ import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException;
import com.lyndir.masterpassword.model.MPUser; import com.lyndir.masterpassword.model.MPUser;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Objects;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.joda.time.Instant; import org.joda.time.Instant;
@ -98,8 +99,10 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
} }
public void setFormat(final MPMarshalFormat format) { public void setFormat(final MPMarshalFormat format) {
this.format = format; if (Objects.equals(this.format, format))
return;
this.format = format;
setChanged(); setChanged();
} }
@ -108,8 +111,10 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
} }
public void setContentMode(final MPMarshaller.ContentMode contentMode) { public void setContentMode(final MPMarshaller.ContentMode contentMode) {
this.contentMode = contentMode; if (Objects.equals(this.contentMode, contentMode))
return;
this.contentMode = contentMode;
setChanged(); setChanged();
} }
@ -118,8 +123,10 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
} }
public void setDefaultType(final MPResultType defaultType) { public void setDefaultType(final MPResultType defaultType) {
this.defaultType = defaultType; if (Objects.equals(this.defaultType, defaultType))
return;
this.defaultType = defaultType;
setChanged(); setChanged();
} }
@ -129,7 +136,6 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
public void use() { public void use() {
lastUsed = new Instant(); lastUsed = new Instant();
setChanged(); setChanged();
} }
@ -159,7 +165,6 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
if (keyID == null) { if (keyID == null) {
keyID = masterKey.getKeyID( getAlgorithm() ); keyID = masterKey.getKeyID( getAlgorithm() );
setChanged(); setChanged();
} }
} }

View File

@ -25,8 +25,8 @@ import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.model.MPConstants; import com.lyndir.masterpassword.model.MPConstants;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.*;
import java.util.Map; import java.util.concurrent.CopyOnWriteArraySet;
/** /**
@ -52,6 +52,7 @@ public class MPFileUserManager {
} }
} }
private final Collection<Listener> listeners = new CopyOnWriteArraySet<>();
private final Map<String, MPFileUser> userByName = new HashMap<>(); private final Map<String, MPFileUser> userByName = new HashMap<>();
private final File path; private final File path;
@ -67,13 +68,13 @@ public class MPFileUserManager {
this.path = path; this.path = path;
} }
public ImmutableSortedSet<MPFileUser> reload() { public void reload() {
userByName.clear(); userByName.clear();
File[] pathFiles; File[] pathFiles;
if ((!path.exists() && !path.mkdirs()) || ((pathFiles = path.listFiles()) == null)) { if ((!path.exists() && !path.mkdirs()) || ((pathFiles = path.listFiles()) == null)) {
logger.err( "Couldn't create directory for user files: %s", path ); logger.err( "Couldn't create directory for user files: %s", path );
return getFiles(); return;
} }
for (final File file : pathFiles) for (final File file : pathFiles)
@ -90,12 +91,14 @@ public class MPFileUserManager {
logger.err( e, "Couldn't read user from: %s", file ); logger.err( e, "Couldn't read user from: %s", file );
} }
return getFiles(); fireUpdated();
} }
public MPFileUser add(final String fullName) { public MPFileUser add(final String fullName) {
MPFileUser user = new MPFileUser( fullName ); MPFileUser user = new MPFileUser( fullName );
userByName.put( user.getFullName(), user ); userByName.put( user.getFullName(), user );
fireUpdated();
return user; return user;
} }
@ -104,8 +107,8 @@ public class MPFileUserManager {
File userFile = user.getFile(); File userFile = user.getFile();
if (userFile.exists() && !userFile.delete()) if (userFile.exists() && !userFile.delete())
logger.err( "Couldn't delete file: %s", userFile ); logger.err( "Couldn't delete file: %s", userFile );
else else if (userByName.values().remove( user ))
userByName.values().remove( user ); fireUpdated();
} }
public File getPath() { public File getPath() {
@ -115,4 +118,27 @@ public class MPFileUserManager {
public ImmutableSortedSet<MPFileUser> getFiles() { public ImmutableSortedSet<MPFileUser> getFiles() {
return ImmutableSortedSet.copyOf( userByName.values() ); return ImmutableSortedSet.copyOf( userByName.values() );
} }
public boolean addListener(final Listener listener) {
return listeners.add( listener );
}
public boolean removeListener(final Listener listener) {
return listeners.remove( listener );
}
private void fireUpdated() {
if (listeners.isEmpty())
return;
ImmutableSortedSet<MPFileUser> files = getFiles();
for (final Listener listener : listeners)
listener.onFilesUpdated( files );
}
public interface Listener {
void onFilesUpdated(ImmutableSortedSet<MPFileUser> files);
}
} }