Site settings & add sites.
This commit is contained in:
parent
e639137304
commit
46d301df94
@ -6,7 +6,7 @@ description = 'Master Password Algorithm Implementation'
|
|||||||
|
|
||||||
tasks.withType( JavaCompile ) {
|
tasks.withType( JavaCompile ) {
|
||||||
// Native headers
|
// Native headers
|
||||||
options.compilerArgs += ["-h", new File( new File( project( ':masterpassword-core' ).projectDir, 'src' ), 'java' ).absolutePath]
|
options.compilerArgs += ["-h", new File( new File( project.project( ':masterpassword-core' ).projectDir, 'src' ), 'java' ).absolutePath]
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
|
@ -2,6 +2,7 @@ package com.lyndir.masterpassword.util;
|
|||||||
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
|
||||||
@ -18,6 +19,14 @@ public final class Utilities {
|
|||||||
return consumer.apply( value );
|
return consumer.apply( value );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static <T, R> R ifNotNullElse(@Nullable final T value, final Function<T, R> consumer, @Nonnull final R nullValue) {
|
||||||
|
if (value == null)
|
||||||
|
return nullValue;
|
||||||
|
|
||||||
|
return consumer.apply( value );
|
||||||
|
}
|
||||||
|
|
||||||
public static <T> void ifNotNullDo(@Nullable final T value, final Consumer<T> consumer) {
|
public static <T> void ifNotNullDo(@Nullable final T value, final Consumer<T> consumer) {
|
||||||
if (value != null)
|
if (value != null)
|
||||||
consumer.accept( value );
|
consumer.accept( value );
|
||||||
|
@ -47,8 +47,8 @@ public class GUI {
|
|||||||
private final MasterPasswordFrame frame = new MasterPasswordFrame();
|
private final MasterPasswordFrame frame = new MasterPasswordFrame();
|
||||||
|
|
||||||
public static void main(final String... args) {
|
public static void main(final String... args) {
|
||||||
Thread.setDefaultUncaughtExceptionHandler(
|
// Thread.setDefaultUncaughtExceptionHandler(
|
||||||
(t, e) -> logger.bug( e, "Uncaught: %s", e.getLocalizedMessage() ) );
|
// (t, e) -> logger.bug( e, "Uncaught: %s", e.getLocalizedMessage() ) );
|
||||||
|
|
||||||
if (Config.get().checkForUpdates())
|
if (Config.get().checkForUpdates())
|
||||||
checkUpdate();
|
checkUpdate();
|
||||||
|
@ -29,25 +29,15 @@ import javax.annotation.Nullable;
|
|||||||
/**
|
/**
|
||||||
* @author lhunath, 14-12-16
|
* @author lhunath, 14-12-16
|
||||||
*/
|
*/
|
||||||
public class MPIncognitoSite extends MPBasicSite<MPIncognitoQuestion> {
|
public class MPIncognitoSite extends MPBasicSite<MPIncognitoUser, MPIncognitoQuestion> {
|
||||||
|
|
||||||
private final MPIncognitoUser user;
|
public MPIncognitoSite(final MPIncognitoUser user, final String siteName) {
|
||||||
|
this( user, siteName, null, null, null, null );
|
||||||
public MPIncognitoSite(final MPIncognitoUser user, final String name) {
|
|
||||||
this( user, name, null, null, null, null );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MPIncognitoSite(final MPIncognitoUser user, final String name,
|
public MPIncognitoSite(final MPIncognitoUser user, final String siteName,
|
||||||
@Nullable final MPAlgorithm algorithm, @Nullable final UnsignedInteger counter,
|
@Nullable final MPAlgorithm algorithm, @Nullable final UnsignedInteger counter,
|
||||||
@Nullable final MPResultType resultType, @Nullable final MPResultType loginType) {
|
@Nullable final MPResultType resultType, @Nullable final MPResultType loginType) {
|
||||||
super( name, (algorithm == null)? user.getAlgorithm(): algorithm, counter, resultType, loginType );
|
super( user, siteName, (algorithm == null)? user.getAlgorithm(): algorithm, counter, resultType, loginType );
|
||||||
|
|
||||||
this.user = user;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public MPIncognitoUser getUser() {
|
|
||||||
return user;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,4 +37,9 @@ public class MPIncognitoUser extends MPBasicUser<MPIncognitoSite> {
|
|||||||
public byte[] getKeyID() {
|
public byte[] getKeyID() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MPIncognitoSite addSite(final String siteName) {
|
||||||
|
return addSite( new MPIncognitoSite( this, siteName ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.lyndir.masterpassword.gui.model;
|
||||||
|
|
||||||
|
import com.lyndir.masterpassword.model.*;
|
||||||
|
import com.lyndir.masterpassword.model.impl.*;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lhunath, 2018-07-27
|
||||||
|
*/
|
||||||
|
public class MPNewSite extends MPBasicSite<MPUser<?>, MPQuestion> {
|
||||||
|
|
||||||
|
public MPNewSite(final MPUser<?> user, final String siteName) {
|
||||||
|
super( user, siteName, user.getAlgorithm() );
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,8 @@ import javax.swing.event.ListSelectionListener;
|
|||||||
* @author lhunath, 2018-07-19
|
* @author lhunath, 2018-07-19
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class CollectionListModel<E> extends AbstractListModel<E> implements ComboBoxModel<E>, ListSelectionListener {
|
public class CollectionListModel<E> extends AbstractListModel<E>
|
||||||
|
implements ComboBoxModel<E>, ListSelectionListener, Selectable<E, CollectionListModel<E>> {
|
||||||
|
|
||||||
private final List<E> model = new LinkedList<>();
|
private final List<E> model = new LinkedList<>();
|
||||||
@Nullable
|
@Nullable
|
||||||
@ -55,13 +56,16 @@ public class CollectionListModel<E> extends AbstractListModel<E> implements Comb
|
|||||||
* This operation will mutate the internal model to reflect the given model.
|
* This operation will mutate the internal model to reflect the given model.
|
||||||
* The given model will remain untouched and independent from this object.
|
* The given model will remain untouched and independent from this object.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("AssignmentToForLoopParameter")
|
@SuppressWarnings({ "unchecked", "SuspiciousToArrayCall" })
|
||||||
public synchronized void set(final Collection<? extends E> newModel) {
|
public synchronized void set(final Collection<? extends E> elements) {
|
||||||
ImmutableList<? extends E> newModelList = ImmutableList.copyOf( newModel );
|
set( (E[]) elements.toArray( new Object[0] ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("AssignmentToForLoopParameter")
|
||||||
|
public synchronized void set(final E... elements) {
|
||||||
ListIterator<E> oldIt = model.listIterator();
|
ListIterator<E> oldIt = model.listIterator();
|
||||||
for (int from = 0; oldIt.hasNext(); ++from) {
|
for (int from = 0; oldIt.hasNext(); ++from) {
|
||||||
int to = newModelList.indexOf( oldIt.next() );
|
int to = Arrays.binarySearch( elements, oldIt.next() );
|
||||||
|
|
||||||
if (to != from) {
|
if (to != from) {
|
||||||
oldIt.remove();
|
oldIt.remove();
|
||||||
@ -70,9 +74,8 @@ public class CollectionListModel<E> extends AbstractListModel<E> implements Comb
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator<? extends E> newIt = newModelList.iterator();
|
for (int to = 0; to < elements.length; ++to) {
|
||||||
for (int to = 0; newIt.hasNext(); ++to) {
|
E newSite = elements[to];
|
||||||
E newSite = newIt.next();
|
|
||||||
|
|
||||||
if ((to >= model.size()) || !Objects.equals( model.get( to ), newSite )) {
|
if ((to >= model.size()) || !Objects.equals( model.get( to ), newSite )) {
|
||||||
model.add( to, newSite );
|
model.add( to, newSite );
|
||||||
@ -116,6 +119,7 @@ public class CollectionListModel<E> extends AbstractListModel<E> implements Comb
|
|||||||
this.list.setModel( this );
|
this.list.setModel( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public synchronized CollectionListModel<E> selection(@Nullable final Consumer<E> selectionConsumer) {
|
public synchronized CollectionListModel<E> selection(@Nullable final Consumer<E> selectionConsumer) {
|
||||||
this.selectionConsumer = selectionConsumer;
|
this.selectionConsumer = selectionConsumer;
|
||||||
if (selectionConsumer != null)
|
if (selectionConsumer != null)
|
||||||
@ -124,6 +128,7 @@ public class CollectionListModel<E> extends AbstractListModel<E> implements Comb
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public synchronized CollectionListModel<E> selection(@Nullable final E selectedItem, @Nullable final Consumer<E> selectionConsumer) {
|
public synchronized CollectionListModel<E> selection(@Nullable final E selectedItem, @Nullable final Consumer<E> selectionConsumer) {
|
||||||
this.selectionConsumer = null;
|
this.selectionConsumer = null;
|
||||||
setSelectedItem( selectedItem );
|
setSelectedItem( selectedItem );
|
||||||
@ -134,7 +139,11 @@ public class CollectionListModel<E> extends AbstractListModel<E> implements Comb
|
|||||||
@Override
|
@Override
|
||||||
public synchronized void valueChanged(final ListSelectionEvent event) {
|
public synchronized void valueChanged(final ListSelectionEvent event) {
|
||||||
//noinspection ObjectEquality
|
//noinspection ObjectEquality
|
||||||
if ((event.getSource() == list) && (list.getModel() == this))
|
if (!event.getValueIsAdjusting() && (event.getSource() == list) && (list.getModel() == this)) {
|
||||||
selectedItem = list.getSelectedValue();
|
selectedItem = list.getSelectedValue();
|
||||||
|
|
||||||
|
if (selectionConsumer != null)
|
||||||
|
selectionConsumer.accept( selectedItem );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,8 @@ import javax.annotation.Nullable;
|
|||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
import javax.swing.border.CompoundBorder;
|
import javax.swing.border.CompoundBorder;
|
||||||
|
import javax.swing.event.DocumentEvent;
|
||||||
|
import javax.swing.event.DocumentListener;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,6 +43,12 @@ public abstract class Components {
|
|||||||
public static final int SIZE_MARGIN = 12;
|
public static final int SIZE_MARGIN = 12;
|
||||||
public static final int SIZE_PADDING = 8;
|
public static final int SIZE_PADDING = 8;
|
||||||
|
|
||||||
|
public static GradientPanel panel(final Component... components) {
|
||||||
|
GradientPanel panel = panel( BoxLayout.LINE_AXIS, null, components );
|
||||||
|
panel.setLayout( new OverlayLayout( panel ) );
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
public static GradientPanel panel(final int axis, final Component... components) {
|
public static GradientPanel panel(final int axis, final Component... components) {
|
||||||
return panel( axis, null, components );
|
return panel( axis, null, components );
|
||||||
}
|
}
|
||||||
@ -124,6 +132,40 @@ public abstract class Components {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static JTextField textField(@Nullable final String text, @Nullable final Consumer<String> selection) {
|
||||||
|
return new JTextField( text ) {
|
||||||
|
{
|
||||||
|
setBorder( BorderFactory.createCompoundBorder( BorderFactory.createLineBorder( Res.colors().controlBorder(), 1, true ),
|
||||||
|
BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) ) );
|
||||||
|
setFont( Res.fonts().valueFont( TEXT_SIZE_CONTROL ) );
|
||||||
|
setAlignmentX( LEFT_ALIGNMENT );
|
||||||
|
|
||||||
|
if (selection != null)
|
||||||
|
getDocument().addDocumentListener( new DocumentListener() {
|
||||||
|
@Override
|
||||||
|
public void insertUpdate(final DocumentEvent e) {
|
||||||
|
selection.accept( getText() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeUpdate(final DocumentEvent e) {
|
||||||
|
selection.accept( getText() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changedUpdate(final DocumentEvent e) {
|
||||||
|
selection.accept( getText() );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension getMaximumSize() {
|
||||||
|
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static JPasswordField passwordField() {
|
public static JPasswordField passwordField() {
|
||||||
return new JPasswordField() {
|
return new JPasswordField() {
|
||||||
{
|
{
|
||||||
@ -143,18 +185,16 @@ public abstract class Components {
|
|||||||
return new JList<E>( model ) {
|
return new JList<E>( model ) {
|
||||||
{
|
{
|
||||||
setFont( Res.fonts().valueFont( TEXT_SIZE_CONTROL ) );
|
setFont( Res.fonts().valueFont( TEXT_SIZE_CONTROL ) );
|
||||||
setBorder( BorderFactory.createEmptyBorder( 4, 0, 4, 0 ) );
|
|
||||||
setCellRenderer( new DefaultListCellRenderer() {
|
setCellRenderer( new DefaultListCellRenderer() {
|
||||||
{
|
|
||||||
setBorder( BorderFactory.createEmptyBorder( 0, 4, 0, 4 ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings({ "unchecked", "SerializableStoresNonSerializable" })
|
@SuppressWarnings({ "unchecked", "SerializableStoresNonSerializable" })
|
||||||
public Component getListCellRendererComponent(final JList<?> list, final Object value, final int index,
|
public Component getListCellRendererComponent(final JList<?> list, final Object value, final int index,
|
||||||
final boolean isSelected, final boolean cellHasFocus) {
|
final boolean isSelected, final boolean cellHasFocus) {
|
||||||
return super.getListCellRendererComponent(
|
super.getListCellRendererComponent(
|
||||||
list, valueTransformer.apply( (E) value ), index, isSelected, cellHasFocus );
|
list, valueTransformer.apply( (E) value ), index, isSelected, cellHasFocus );
|
||||||
|
setBorder( BorderFactory.createEmptyBorder( 2, 4, 2, 4 ) );
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
setAlignmentX( LEFT_ALIGNMENT );
|
setAlignmentX( LEFT_ALIGNMENT );
|
||||||
@ -248,11 +288,6 @@ public abstract class Components {
|
|||||||
setAlignmentX( LEFT_ALIGNMENT );
|
setAlignmentX( LEFT_ALIGNMENT );
|
||||||
setBorder( null );
|
setBorder( null );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Dimension getMaximumSize() {
|
|
||||||
return new Dimension( 20, getPreferredSize().height );
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,16 +397,15 @@ public abstract class Components {
|
|||||||
setFont( Res.fonts().valueFont( TEXT_SIZE_CONTROL ) );
|
setFont( Res.fonts().valueFont( TEXT_SIZE_CONTROL ) );
|
||||||
setBorder( BorderFactory.createEmptyBorder( 4, 0, 4, 0 ) );
|
setBorder( BorderFactory.createEmptyBorder( 4, 0, 4, 0 ) );
|
||||||
setRenderer( new DefaultListCellRenderer() {
|
setRenderer( new DefaultListCellRenderer() {
|
||||||
{
|
|
||||||
setBorder( BorderFactory.createEmptyBorder( 0, 4, 0, 4 ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings({ "unchecked", "SerializableStoresNonSerializable" })
|
@SuppressWarnings({ "unchecked", "SerializableStoresNonSerializable" })
|
||||||
public Component getListCellRendererComponent(final JList<?> list, final Object value, final int index,
|
public Component getListCellRendererComponent(final JList<?> list, final Object value, final int index,
|
||||||
final boolean isSelected, final boolean cellHasFocus) {
|
final boolean isSelected, final boolean cellHasFocus) {
|
||||||
return super.getListCellRendererComponent(
|
super.getListCellRendererComponent(
|
||||||
list, valueTransformer.apply( (E) value ), index, isSelected, cellHasFocus );
|
list, valueTransformer.apply( (E) value ), index, isSelected, cellHasFocus );
|
||||||
|
setBorder( BorderFactory.createEmptyBorder( 0, 4, 0, 4 ) );
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
putClientProperty( "JComboBox.isPopDown", Boolean.TRUE );
|
putClientProperty( "JComboBox.isPopDown", Boolean.TRUE );
|
||||||
|
@ -34,6 +34,9 @@ import java.util.concurrent.*;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import org.jetbrains.annotations.NonNls;
|
import org.jetbrains.annotations.NonNls;
|
||||||
|
import org.joda.time.ReadableInstant;
|
||||||
|
import org.joda.time.format.DateTimeFormat;
|
||||||
|
import org.joda.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,6 +106,10 @@ public abstract class Res {
|
|||||||
return colors;
|
return colors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String format(final ReadableInstant instant) {
|
||||||
|
return DateTimeFormat.mediumDateTime().print( instant );
|
||||||
|
}
|
||||||
|
|
||||||
public static final class Icons {
|
public static final class Icons {
|
||||||
|
|
||||||
public Icon add() {
|
public Icon add() {
|
||||||
@ -121,6 +128,10 @@ public abstract class Res {
|
|||||||
return icon( "media/icon_user.png" );
|
return icon( "media/icon_user.png" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Icon settings() {
|
||||||
|
return icon( "media/icon_settings.png" );
|
||||||
|
}
|
||||||
|
|
||||||
public Icon avatar(final int index) {
|
public Icon avatar(final int index) {
|
||||||
return icon( strf( "media/avatar-%d.png", index % avatars() ) );
|
return icon( strf( "media/avatar-%d.png", index % avatars() ) );
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.lyndir.masterpassword.gui.util;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lhunath, 2018-07-26
|
||||||
|
*/
|
||||||
|
public interface Selectable<E, T> {
|
||||||
|
|
||||||
|
T selection(@Nullable Consumer<E> selectionConsumer);
|
||||||
|
|
||||||
|
T selection(E selectedItem, @Nullable Consumer<E> selectionConsumer);
|
||||||
|
}
|
@ -19,16 +19,24 @@
|
|||||||
package com.lyndir.masterpassword.gui.util;
|
package com.lyndir.masterpassword.gui.util;
|
||||||
|
|
||||||
import com.google.common.primitives.UnsignedInteger;
|
import com.google.common.primitives.UnsignedInteger;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author lhunath, 2016-10-29
|
* @author lhunath, 2016-10-29
|
||||||
*/
|
*/
|
||||||
public class UnsignedIntegerModel extends SpinnerNumberModel {
|
public class UnsignedIntegerModel extends SpinnerNumberModel
|
||||||
|
implements Selectable<UnsignedInteger, UnsignedIntegerModel> {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private ChangeListener changeListener;
|
||||||
|
|
||||||
public UnsignedIntegerModel() {
|
public UnsignedIntegerModel() {
|
||||||
this( UnsignedInteger.ZERO, UnsignedInteger.ZERO, UnsignedInteger.MAX_VALUE, UnsignedInteger.ONE );
|
this( UnsignedInteger.ZERO, UnsignedInteger.ZERO, UnsignedInteger.MAX_VALUE, UnsignedInteger.ONE );
|
||||||
}
|
}
|
||||||
@ -55,4 +63,30 @@ public class UnsignedIntegerModel extends SpinnerNumberModel {
|
|||||||
public UnsignedInteger getNumber() {
|
public UnsignedInteger getNumber() {
|
||||||
return (UnsignedInteger) super.getNumber();
|
return (UnsignedInteger) super.getNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UnsignedIntegerModel selection(@Nullable final Consumer<UnsignedInteger> selectionConsumer) {
|
||||||
|
if (changeListener != null) {
|
||||||
|
removeChangeListener( changeListener );
|
||||||
|
changeListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectionConsumer != null) {
|
||||||
|
addChangeListener( changeListener = e -> selectionConsumer.accept( getNumber() ) );
|
||||||
|
selectionConsumer.accept( getNumber() );
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UnsignedIntegerModel selection(final UnsignedInteger selectedItem, @Nullable final Consumer<UnsignedInteger> selectionConsumer) {
|
||||||
|
if (changeListener != null) {
|
||||||
|
removeChangeListener( changeListener );
|
||||||
|
changeListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue( selectedItem );
|
||||||
|
return selection( selectionConsumer );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@ public class MasterPasswordFrame extends JFrame implements FilesPanel.Listener,
|
|||||||
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
|
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
|
||||||
setContentPane( root = Components.borderPanel( Res.colors().frameBg(), BoxLayout.PAGE_AXIS ) );
|
setContentPane( root = Components.borderPanel( Res.colors().frameBg(), BoxLayout.PAGE_AXIS ) );
|
||||||
root.add( filesPanel );
|
root.add( filesPanel );
|
||||||
root.add( new JSeparator( SwingConstants.HORIZONTAL ) );
|
|
||||||
root.add( Components.strut() );
|
root.add( Components.strut() );
|
||||||
root.add( Components.borderPanel(
|
root.add( Components.borderPanel(
|
||||||
BorderFactory.createBevelBorder( BevelBorder.RAISED, Res.colors().controlBorder(), Res.colors().frameBg() ),
|
BorderFactory.createBevelBorder( BevelBorder.RAISED, Res.colors().controlBorder(), Res.colors().frameBg() ),
|
||||||
|
@ -2,14 +2,14 @@ package com.lyndir.masterpassword.gui.view;
|
|||||||
|
|
||||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableCollection;
|
import com.google.common.base.Joiner;
|
||||||
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.primitives.UnsignedInteger;
|
import com.google.common.primitives.UnsignedInteger;
|
||||||
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.gui.util.Res;
|
import com.lyndir.masterpassword.gui.model.MPNewSite;
|
||||||
import com.lyndir.masterpassword.gui.util.CollectionListModel;
|
import com.lyndir.masterpassword.gui.util.*;
|
||||||
import com.lyndir.masterpassword.gui.util.Components;
|
|
||||||
import com.lyndir.masterpassword.model.*;
|
import com.lyndir.masterpassword.model.*;
|
||||||
import com.lyndir.masterpassword.model.impl.MPFileSite;
|
import com.lyndir.masterpassword.model.impl.MPFileSite;
|
||||||
import com.lyndir.masterpassword.model.impl.MPFileUser;
|
import com.lyndir.masterpassword.model.impl.MPFileUser;
|
||||||
@ -17,15 +17,15 @@ import java.awt.*;
|
|||||||
import java.awt.datatransfer.StringSelection;
|
import java.awt.datatransfer.StringSelection;
|
||||||
import java.awt.datatransfer.Transferable;
|
import java.awt.datatransfer.Transferable;
|
||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
import java.util.Objects;
|
import java.util.*;
|
||||||
import java.util.Random;
|
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.event.*;
|
import javax.swing.event.DocumentEvent;
|
||||||
|
import javax.swing.event.DocumentListener;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -166,7 +166,7 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
|
|||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void update() {
|
private synchronized void update() {
|
||||||
errorLabel.setText( null );
|
errorLabel.setText( " " );
|
||||||
|
|
||||||
if (identiconJob != null)
|
if (identiconJob != null)
|
||||||
identiconJob.cancel( true );
|
identiconJob.cancel( true );
|
||||||
@ -193,38 +193,47 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static final class AuthenticatedUserPanel extends JPanel implements ActionListener, DocumentListener, ListSelectionListener,
|
private static final class AuthenticatedUserPanel extends JPanel implements KeyListener {
|
||||||
KeyListener {
|
|
||||||
|
|
||||||
public static final int SIZE_RESULT = 48;
|
public static final int SIZE_RESULT = 48;
|
||||||
|
|
||||||
@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 JLabel queryLabel = Components.label();
|
private final JButton passwordButton =
|
||||||
private final JTextField queryField = Components.textField();
|
Components.button( Res.icons().settings(), event -> showSiteSettings() );
|
||||||
private final CollectionListModel<MPSite<?>> sitesModel = new CollectionListModel<>();
|
private final JLabel queryLabel = Components.label();
|
||||||
private final JList<MPSite<?>> sitesList = Components.list( sitesModel,
|
private final JTextField queryField = Components.textField( null, this::updateSites );
|
||||||
value -> (value != null)? value.getName(): null );
|
private final CollectionListModel<MPSite<?>> sitesModel =
|
||||||
private Future<?> updateSitesJob;
|
new CollectionListModel<MPSite<?>>().selection( this::showSiteResult );
|
||||||
|
private final JList<MPSite<?>> sitesList =
|
||||||
|
Components.list( sitesModel, this::getSiteDescription );
|
||||||
|
|
||||||
|
private Future<?> updateSitesJob;
|
||||||
|
|
||||||
private AuthenticatedUserPanel(@Nonnull final MPUser<?> user) {
|
private AuthenticatedUserPanel(@Nonnull final MPUser<?> user) {
|
||||||
setLayout( new BoxLayout( this, BoxLayout.PAGE_AXIS ) );
|
setLayout( new BoxLayout( this, BoxLayout.PAGE_AXIS ) );
|
||||||
|
|
||||||
this.user = user;
|
this.user = user;
|
||||||
|
|
||||||
add( new Components.GradientPanel( new BorderLayout() ) {
|
add( Components.panel(
|
||||||
{
|
Components.heading( user.getFullName(), SwingConstants.CENTER ),
|
||||||
add( Components.heading( user.getFullName(), SwingConstants.CENTER ), BorderLayout.CENTER );
|
Components.panel(
|
||||||
add( Components.button( Res.icons().user(), event -> showPreferences() ), BorderLayout.LINE_END );
|
BoxLayout.LINE_AXIS,
|
||||||
}
|
Box.createGlue(),
|
||||||
} );
|
Components.button( Res.icons().user(), event -> showUserPreferences() ) ) ) );
|
||||||
|
|
||||||
add( passwordLabel );
|
add( passwordLabel );
|
||||||
add( passwordField );
|
add( Components.panel(
|
||||||
|
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() );
|
||||||
|
|
||||||
@ -232,18 +241,16 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
|
|||||||
queryLabel.setText( strf( "%s's password for:", user.getFullName() ) );
|
queryLabel.setText( strf( "%s's password for:", user.getFullName() ) );
|
||||||
add( queryField );
|
add( queryField );
|
||||||
queryField.putClientProperty( "JTextField.variant", "search" );
|
queryField.putClientProperty( "JTextField.variant", "search" );
|
||||||
queryField.addActionListener( this );
|
queryField.addActionListener( event -> useSite() );
|
||||||
queryField.addKeyListener( this );
|
queryField.addKeyListener( this );
|
||||||
queryField.getDocument().addDocumentListener( this );
|
|
||||||
queryField.requestFocusInWindow();
|
queryField.requestFocusInWindow();
|
||||||
add( Components.strut() );
|
add( Components.strut() );
|
||||||
add( Components.scrollPane( sitesList ) );
|
add( Components.scrollPane( sitesList ) );
|
||||||
sitesModel.registerList( sitesList );
|
sitesModel.registerList( sitesList );
|
||||||
sitesList.addListSelectionListener( this );
|
|
||||||
add( Box.createGlue() );
|
add( Box.createGlue() );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showPreferences() {
|
public void showUserPreferences() {
|
||||||
ImmutableList.Builder<Component> components = ImmutableList.builder();
|
ImmutableList.Builder<Component> components = ImmutableList.builder();
|
||||||
|
|
||||||
MPFileUser fileUser = (user instanceof MPFileUser)? (MPFileUser) user: null;
|
MPFileUser fileUser = (user instanceof MPFileUser)? (MPFileUser) user: null;
|
||||||
@ -262,9 +269,79 @@ 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] ) ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void showSiteSettings() {
|
||||||
public void actionPerformed(final ActionEvent event) {
|
ImmutableList.Builder<Component> components = ImmutableList.builder();
|
||||||
MPSite<?> site = sitesList.getSelectedValue();
|
|
||||||
|
MPSite<?> site = sitesModel.getSelectedItem();
|
||||||
|
if (site == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
components.add( Components.label( "Algorithm:" ),
|
||||||
|
Components.comboBox( MPAlgorithm.Version.values(), MPAlgorithm.Version::name,
|
||||||
|
site.getAlgorithm().version(),
|
||||||
|
version -> site.setAlgorithm( version.getAlgorithm() ) ) );
|
||||||
|
|
||||||
|
components.add( Components.label( "Counter:" ),
|
||||||
|
Components.spinner( new UnsignedIntegerModel( site.getCounter(), UnsignedInteger.ONE )
|
||||||
|
.selection( site::setCounter ) ),
|
||||||
|
Components.strut() );
|
||||||
|
|
||||||
|
components.add( Components.label( "Password Type:" ),
|
||||||
|
Components.comboBox( MPResultType.values(), MPResultType::getLongName,
|
||||||
|
site.getResultType(), site::setResultType ),
|
||||||
|
Components.strut() );
|
||||||
|
|
||||||
|
components.add( Components.label( "Login Type:" ),
|
||||||
|
Components.comboBox( MPResultType.values(), MPResultType::getLongName,
|
||||||
|
site.getLoginType(), site::setLoginType ),
|
||||||
|
Components.strut() );
|
||||||
|
|
||||||
|
MPFileSite fileSite = (site instanceof MPFileSite)? (MPFileSite) site: null;
|
||||||
|
if (fileSite != null)
|
||||||
|
components.add( Components.label( "URL:" ),
|
||||||
|
Components.textField( fileSite.getUrl(), fileSite::setUrl ),
|
||||||
|
Components.strut() );
|
||||||
|
|
||||||
|
Components.showDialog( this, site.getSiteName(), new JOptionPane( Components.panel(
|
||||||
|
BoxLayout.PAGE_AXIS, components.build().toArray( new Component[0] ) ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSiteDescription(@Nonnull final MPSite<?> site) {
|
||||||
|
if (site instanceof MPNewSite)
|
||||||
|
return strf( "<html><strong>%s</strong> <Add new site></html>", queryField.getText() );
|
||||||
|
|
||||||
|
ImmutableList.Builder<Object> parameters = ImmutableList.builder();
|
||||||
|
try {
|
||||||
|
MPFileSite fileSite = (site instanceof MPFileSite)? (MPFileSite) site: null;
|
||||||
|
if (fileSite != null)
|
||||||
|
parameters.add( Res.format( fileSite.getLastUsed() ) );
|
||||||
|
parameters.add( site.getAlgorithm().version() );
|
||||||
|
parameters.add( strf( "#%d", site.getCounter().longValue() ) );
|
||||||
|
parameters.add( strf( "<em>%s</em>", site.getLogin() ) );
|
||||||
|
if ((fileSite != null) && (fileSite.getUrl() != null))
|
||||||
|
parameters.add( fileSite.getUrl() );
|
||||||
|
}
|
||||||
|
catch (final MPAlgorithmException | MPKeyUnavailableException e) {
|
||||||
|
logger.err( e, "While generating site description." );
|
||||||
|
parameters.add( e.getLocalizedMessage() );
|
||||||
|
}
|
||||||
|
|
||||||
|
return strf( "<html><strong>%s</strong> (%s)</html>", site.getSiteName(),
|
||||||
|
Joiner.on( " - " ).skipNulls().join( parameters.build() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void useSite() {
|
||||||
|
MPSite<?> site = sitesModel.getSelectedItem();
|
||||||
|
if (site instanceof MPNewSite) {
|
||||||
|
if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(
|
||||||
|
this, strf( "<html>Remember the site [<strong>%s</strong>]?</html>", site.getSiteName() ),
|
||||||
|
"New Site", JOptionPane.YES_NO_OPTION )) {
|
||||||
|
sitesModel.setSelectedItem( user.addSite( site.getSiteName() ) );
|
||||||
|
useSite();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
showSiteResult( site, result -> {
|
showSiteResult( site, result -> {
|
||||||
if (result == null)
|
if (result == null)
|
||||||
return;
|
return;
|
||||||
@ -282,24 +359,8 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
|
|||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void showSiteResult(@Nullable final MPSite<?> site) {
|
||||||
public void insertUpdate(final DocumentEvent event) {
|
showSiteResult( site, null );
|
||||||
updateSites();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeUpdate(final DocumentEvent event) {
|
|
||||||
updateSites();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void changedUpdate(final DocumentEvent event) {
|
|
||||||
updateSites();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void valueChanged(final ListSelectionEvent event) {
|
|
||||||
showSiteResult( event.getValueIsAdjusting()? null: sitesList.getSelectedValue(), null );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showSiteResult(@Nullable final MPSite<?> site, @Nullable final Consumer<String> resultCallback) {
|
private void showSiteResult(@Nullable final MPSite<?> site, @Nullable final Consumer<String> resultCallback) {
|
||||||
@ -309,22 +370,21 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
|
|||||||
Res.ui( () -> {
|
Res.ui( () -> {
|
||||||
passwordLabel.setText( " " );
|
passwordLabel.setText( " " );
|
||||||
passwordField.setText( " " );
|
passwordField.setText( " " );
|
||||||
|
passwordButton.setVisible( false );
|
||||||
} );
|
} );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String siteName = site.getName();
|
|
||||||
Res.job( () -> {
|
Res.job( () -> {
|
||||||
try {
|
try {
|
||||||
String result = user.getMasterKey().siteResult(
|
String result = site.getResult();
|
||||||
siteName, user.getAlgorithm(), UnsignedInteger.ONE,
|
|
||||||
MPKeyPurpose.Authentication, null, MPResultType.GeneratedLong, null );
|
|
||||||
if (resultCallback != null)
|
if (resultCallback != null)
|
||||||
resultCallback.accept( result );
|
resultCallback.accept( result );
|
||||||
|
|
||||||
Res.ui( () -> {
|
Res.ui( () -> {
|
||||||
passwordLabel.setText( strf( "Your password for %s:", siteName ) );
|
passwordLabel.setText( strf( "Your password for %s:", site.getSiteName() ) );
|
||||||
passwordField.setText( result );
|
passwordField.setText( result );
|
||||||
|
passwordButton.setVisible( true );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
catch (final MPKeyUnavailableException | MPAlgorithmException e) {
|
catch (final MPKeyUnavailableException | MPAlgorithmException e) {
|
||||||
@ -349,12 +409,19 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
|
|||||||
sitesList.dispatchEvent( event );
|
sitesList.dispatchEvent( event );
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void updateSites() {
|
private synchronized void updateSites(@Nullable final String query) {
|
||||||
if (updateSitesJob != null)
|
if (updateSitesJob != null)
|
||||||
updateSitesJob.cancel( true );
|
updateSitesJob.cancel( true );
|
||||||
|
|
||||||
updateSitesJob = Res.job( () -> {
|
updateSitesJob = Res.job( () -> {
|
||||||
ImmutableCollection<? extends MPSite<?>> sites = user.findSites( queryField.getText() );
|
Collection<MPSite<?>> sites = new LinkedList<>();
|
||||||
|
if (!Strings.isNullOrEmpty( query )) {
|
||||||
|
sites.addAll( new LinkedList<>( user.findSites( query ) ) );
|
||||||
|
|
||||||
|
if (sites.stream().noneMatch( site -> site.getSiteName().equalsIgnoreCase( query ) ))
|
||||||
|
sites.add( new MPNewSite( user, query ) );
|
||||||
|
}
|
||||||
|
|
||||||
Res.ui( () -> sitesModel.set( sites ) );
|
Res.ui( () -> sitesModel.set( sites ) );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
@ -33,7 +33,7 @@ public interface MPSite<Q extends MPQuestion> extends Comparable<MPSite<?>> {
|
|||||||
// - Meta
|
// - Meta
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
String getName();
|
String getSiteName();
|
||||||
|
|
||||||
// - Algorithm
|
// - Algorithm
|
||||||
|
|
||||||
@ -57,10 +57,34 @@ public interface MPSite<Q extends MPQuestion> extends Comparable<MPSite<?>> {
|
|||||||
|
|
||||||
void setLoginType(@Nullable MPResultType loginType);
|
void setLoginType(@Nullable MPResultType loginType);
|
||||||
|
|
||||||
|
default String getResult()
|
||||||
|
throws MPKeyUnavailableException, MPAlgorithmException {
|
||||||
|
|
||||||
|
return getResult( MPKeyPurpose.Authentication );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
default String getResult(final MPKeyPurpose keyPurpose)
|
||||||
|
throws MPKeyUnavailableException, MPAlgorithmException {
|
||||||
|
return getResult( keyPurpose, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
default String getResult(final MPKeyPurpose keyPurpose, @Nullable final String keyContext)
|
||||||
|
throws MPKeyUnavailableException, MPAlgorithmException {
|
||||||
|
return getResult( keyPurpose, keyContext, null );
|
||||||
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
String getResult(MPKeyPurpose keyPurpose, @Nullable String keyContext, @Nullable String state)
|
String getResult(MPKeyPurpose keyPurpose, @Nullable String keyContext, @Nullable String state)
|
||||||
throws MPKeyUnavailableException, MPAlgorithmException;
|
throws MPKeyUnavailableException, MPAlgorithmException;
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
default String getLogin()
|
||||||
|
throws MPKeyUnavailableException, MPAlgorithmException {
|
||||||
|
return getLogin( null );
|
||||||
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
String getLogin(@Nullable String state)
|
String getLogin(@Nullable String state)
|
||||||
throws MPKeyUnavailableException, MPAlgorithmException;
|
throws MPKeyUnavailableException, MPAlgorithmException;
|
||||||
|
@ -20,8 +20,6 @@ package com.lyndir.masterpassword.model;
|
|||||||
|
|
||||||
import com.google.common.collect.ImmutableCollection;
|
import com.google.common.collect.ImmutableCollection;
|
||||||
import com.lyndir.masterpassword.*;
|
import com.lyndir.masterpassword.*;
|
||||||
import com.lyndir.masterpassword.model.impl.MPBasicSite;
|
|
||||||
import com.lyndir.masterpassword.model.impl.MPBasicUser;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -87,7 +85,10 @@ public interface MPUser<S extends MPSite<?>> extends Comparable<MPUser<?>> {
|
|||||||
|
|
||||||
// - Relations
|
// - Relations
|
||||||
|
|
||||||
void addSite(S site);
|
S addSite(String siteName);
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
S addSite(S site);
|
||||||
|
|
||||||
void deleteSite(S site);
|
void deleteSite(S site);
|
||||||
|
|
||||||
@ -95,7 +96,7 @@ public interface MPUser<S extends MPSite<?>> extends Comparable<MPUser<?>> {
|
|||||||
Collection<S> getSites();
|
Collection<S> getSites();
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
ImmutableCollection<S> findSites(String query);
|
ImmutableCollection<S> findSites(@Nullable String query);
|
||||||
|
|
||||||
boolean addListener(Listener listener);
|
boolean addListener(Listener listener);
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ public abstract class MPBasicQuestion extends Changeable implements MPQuestion {
|
|||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public abstract MPBasicSite<?> getSite();
|
public abstract MPBasicSite<?, ?> getSite();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onChanged() {
|
protected void onChanged() {
|
||||||
|
@ -23,34 +23,36 @@ import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
|||||||
|
|
||||||
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.MPQuestion;
|
import com.lyndir.masterpassword.model.*;
|
||||||
import com.lyndir.masterpassword.model.MPSite;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author lhunath, 14-12-16
|
* @author lhunath, 14-12-16
|
||||||
*/
|
*/
|
||||||
public abstract class MPBasicSite<Q extends MPQuestion> extends Changeable implements MPSite<Q> {
|
public abstract class MPBasicSite<U extends MPUser<?>, Q extends MPQuestion> extends Changeable
|
||||||
|
implements MPSite<Q> {
|
||||||
|
|
||||||
|
private final Collection<Q> questions = new LinkedHashSet<>();
|
||||||
|
private final U user;
|
||||||
|
private final String siteName;
|
||||||
|
|
||||||
private String name;
|
|
||||||
private MPAlgorithm algorithm;
|
private MPAlgorithm algorithm;
|
||||||
private UnsignedInteger counter;
|
private UnsignedInteger counter;
|
||||||
private MPResultType resultType;
|
private MPResultType resultType;
|
||||||
private MPResultType loginType;
|
private MPResultType loginType;
|
||||||
|
|
||||||
private final Collection<Q> questions = new LinkedHashSet<>();
|
protected MPBasicSite(final U user, final String siteName, final MPAlgorithm algorithm) {
|
||||||
|
this( user, siteName, algorithm, null, null, null );
|
||||||
protected MPBasicSite(final String name, final MPAlgorithm algorithm) {
|
|
||||||
this( name, algorithm, null, null, null );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected MPBasicSite(final String name, final MPAlgorithm algorithm, @Nullable final UnsignedInteger counter,
|
protected MPBasicSite(final U user, final String siteName, final MPAlgorithm algorithm,
|
||||||
|
@Nullable final UnsignedInteger counter,
|
||||||
@Nullable final MPResultType resultType, @Nullable final MPResultType loginType) {
|
@Nullable final MPResultType resultType, @Nullable final MPResultType loginType) {
|
||||||
this.name = name;
|
this.user = user;
|
||||||
|
this.siteName = siteName;
|
||||||
this.algorithm = algorithm;
|
this.algorithm = algorithm;
|
||||||
this.counter = (counter == null)? algorithm.mpw_default_counter(): counter;
|
this.counter = (counter == null)? algorithm.mpw_default_counter(): counter;
|
||||||
this.resultType = (resultType == null)? algorithm.mpw_default_result_type(): resultType;
|
this.resultType = (resultType == null)? algorithm.mpw_default_result_type(): resultType;
|
||||||
@ -59,8 +61,8 @@ public abstract class MPBasicSite<Q extends MPQuestion> extends Changeable imple
|
|||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getSiteName() {
|
||||||
return name;
|
return siteName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@ -128,7 +130,7 @@ public abstract class MPBasicSite<Q extends MPQuestion> extends Changeable imple
|
|||||||
throws MPKeyUnavailableException, MPAlgorithmException {
|
throws MPKeyUnavailableException, MPAlgorithmException {
|
||||||
|
|
||||||
return getUser().getMasterKey().siteResult(
|
return getUser().getMasterKey().siteResult(
|
||||||
getName(), getAlgorithm(), ifNotNullElse( counter, getAlgorithm().mpw_default_counter() ),
|
getSiteName(), getAlgorithm(), ifNotNullElse( counter, getAlgorithm().mpw_default_counter() ),
|
||||||
keyPurpose, keyContext, type, state );
|
keyPurpose, keyContext, type, state );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,7 +139,7 @@ public abstract class MPBasicSite<Q extends MPQuestion> extends Changeable imple
|
|||||||
throws MPKeyUnavailableException, MPAlgorithmException {
|
throws MPKeyUnavailableException, MPAlgorithmException {
|
||||||
|
|
||||||
return getUser().getMasterKey().siteState(
|
return getUser().getMasterKey().siteState(
|
||||||
getName(), getAlgorithm(), ifNotNullElse( counter, getAlgorithm().mpw_default_counter() ),
|
getSiteName(), getAlgorithm(), ifNotNullElse( counter, getAlgorithm().mpw_default_counter() ),
|
||||||
keyPurpose, keyContext, type, state );
|
keyPurpose, keyContext, type, state );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,32 +173,35 @@ public abstract class MPBasicSite<Q extends MPQuestion> extends Changeable imple
|
|||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public abstract MPBasicUser<?> getUser();
|
public U getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onChanged() {
|
protected void onChanged() {
|
||||||
super.onChanged();
|
super.onChanged();
|
||||||
|
|
||||||
getUser().setChanged();
|
if (user instanceof Changeable)
|
||||||
|
((Changeable) user).setChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hashCode( getName() );
|
return Objects.hashCode( getSiteName() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(final Object obj) {
|
public boolean equals(final Object obj) {
|
||||||
return (this == obj) || ((obj instanceof MPSite) && Objects.equals( getName(), ((MPSite<?>) obj).getName() ));
|
return (this == obj) || ((obj instanceof MPSite) && Objects.equals( getSiteName(), ((MPSite<?>) obj).getSiteName() ));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(@NotNull final MPSite<?> o) {
|
public int compareTo(@Nonnull final MPSite<?> o) {
|
||||||
return getName().compareTo( o.getName() );
|
return getSiteName().compareTo( o.getSiteName() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return strf( "{%s: %s}", getClass().getSimpleName(), getName() );
|
return strf( "{%s: %s}", getClass().getSimpleName(), getSiteName() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ import javax.annotation.Nullable;
|
|||||||
/**
|
/**
|
||||||
* @author lhunath, 2014-06-08
|
* @author lhunath, 2014-06-08
|
||||||
*/
|
*/
|
||||||
public abstract class MPBasicUser<S extends MPBasicSite<?>> extends Changeable implements MPUser<S> {
|
public abstract class MPBasicUser<S extends MPBasicSite<?, ?>> extends Changeable implements MPUser<S> {
|
||||||
|
|
||||||
protected final Logger logger = Logger.get( getClass() );
|
protected final Logger logger = Logger.get( getClass() );
|
||||||
private final Set<Listener> listeners = new CopyOnWriteArraySet<>();
|
private final Set<Listener> listeners = new CopyOnWriteArraySet<>();
|
||||||
@ -152,10 +152,11 @@ public abstract class MPBasicUser<S extends MPBasicSite<?>> extends Changeable i
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addSite(final S site) {
|
public S addSite(final S site) {
|
||||||
sites.put( site.getName(), site );
|
sites.put( site.getSiteName(), site );
|
||||||
|
|
||||||
setChanged();
|
setChanged();
|
||||||
|
return site;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -173,11 +174,12 @@ public abstract class MPBasicUser<S extends MPBasicSite<?>> extends Changeable i
|
|||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public ImmutableCollection<S> findSites(final String query) {
|
public ImmutableCollection<S> findSites(@Nullable final String query) {
|
||||||
ImmutableSortedSet.Builder<S> results = ImmutableSortedSet.naturalOrder();
|
ImmutableSortedSet.Builder<S> results = ImmutableSortedSet.naturalOrder();
|
||||||
for (final S site : getSites())
|
if (query != null)
|
||||||
if (site.getName().startsWith( query ))
|
for (final S site : getSites())
|
||||||
results.add( site );
|
if (site.getSiteName().startsWith( query ))
|
||||||
|
results.add( site );
|
||||||
|
|
||||||
return results.build();
|
return results.build();
|
||||||
}
|
}
|
||||||
@ -211,7 +213,7 @@ public abstract class MPBasicUser<S extends MPBasicSite<?>> extends Changeable i
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(final MPUser<?> o) {
|
public int compareTo(@Nonnull final MPUser<?> o) {
|
||||||
return getFullName().compareTo( o.getFullName() );
|
return getFullName().compareTo( o.getFullName() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,9 +31,7 @@ import org.joda.time.ReadableInstant;
|
|||||||
* @author lhunath, 14-12-05
|
* @author lhunath, 14-12-05
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("ComparableImplementedButEqualsNotOverridden")
|
@SuppressWarnings("ComparableImplementedButEqualsNotOverridden")
|
||||||
public class MPFileSite extends MPBasicSite<MPFileQuestion> {
|
public class MPFileSite extends MPBasicSite<MPFileUser, MPFileQuestion> {
|
||||||
|
|
||||||
private final MPFileUser user;
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private String url;
|
private String url;
|
||||||
@ -61,10 +59,9 @@ public class MPFileSite extends MPBasicSite<MPFileQuestion> {
|
|||||||
@Nullable final MPResultType resultType, @Nullable final String resultState,
|
@Nullable final MPResultType resultType, @Nullable final String resultState,
|
||||||
@Nullable final MPResultType loginType, @Nullable final String loginState,
|
@Nullable final MPResultType loginType, @Nullable final String loginState,
|
||||||
@Nullable final String url, final int uses, final ReadableInstant lastUsed) {
|
@Nullable final String url, final int uses, final ReadableInstant lastUsed) {
|
||||||
super( name, (algorithm == null)? user.getAlgorithm(): algorithm, counter,
|
super( user, name, (algorithm == null)? user.getAlgorithm(): algorithm, counter,
|
||||||
(resultType == null)? user.getDefaultType(): resultType, loginType );
|
(resultType == null)? user.getDefaultType(): resultType, loginType );
|
||||||
|
|
||||||
this.user = user;
|
|
||||||
this.resultState = resultState;
|
this.resultState = resultState;
|
||||||
this.loginState = loginState;
|
this.loginState = loginState;
|
||||||
this.url = url;
|
this.url = url;
|
||||||
@ -94,23 +91,21 @@ public class MPFileSite extends MPBasicSite<MPFileQuestion> {
|
|||||||
public void use() {
|
public void use() {
|
||||||
uses++;
|
uses++;
|
||||||
lastUsed = new Instant();
|
lastUsed = new Instant();
|
||||||
user.use();
|
getUser().use();
|
||||||
|
|
||||||
setChanged();
|
setChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getResult()
|
@Nonnull
|
||||||
throws MPKeyUnavailableException, MPAlgorithmException {
|
@Override
|
||||||
|
|
||||||
return getResult( MPKeyPurpose.Authentication, null );
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getResult(final MPKeyPurpose keyPurpose, @Nullable final String keyContext)
|
public String getResult(final MPKeyPurpose keyPurpose, @Nullable final String keyContext)
|
||||||
throws MPKeyUnavailableException, MPAlgorithmException {
|
throws MPKeyUnavailableException, MPAlgorithmException {
|
||||||
|
|
||||||
return getResult( keyPurpose, keyContext, getResultState() );
|
return getResult( keyPurpose, keyContext, getResultState() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
public String getLogin()
|
public String getLogin()
|
||||||
throws MPKeyUnavailableException, MPAlgorithmException {
|
throws MPKeyUnavailableException, MPAlgorithmException {
|
||||||
|
|
||||||
@ -153,14 +148,8 @@ public class MPFileSite extends MPBasicSite<MPFileQuestion> {
|
|||||||
setChanged();
|
setChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public MPFileUser getUser() {
|
public int compareTo(@Nonnull final MPSite<?> o) {
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo(final MPSite<?> o) {
|
|
||||||
int comparison = (o instanceof MPFileSite)? ((MPFileSite) o).getLastUsed().compareTo( getLastUsed() ): 0;
|
int comparison = (o instanceof MPFileSite)? ((MPFileSite) o).getLastUsed().compareTo( getLastUsed() ): 0;
|
||||||
if (comparison != 0)
|
if (comparison != 0)
|
||||||
return comparison;
|
return comparison;
|
||||||
|
@ -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 javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import org.joda.time.Instant;
|
import org.joda.time.Instant;
|
||||||
import org.joda.time.ReadableInstant;
|
import org.joda.time.ReadableInstant;
|
||||||
@ -163,6 +164,11 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MPFileSite addSite(final String siteName) {
|
||||||
|
return addSite( new MPFileSite( this, siteName ) );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onChanged() {
|
protected void onChanged() {
|
||||||
try {
|
try {
|
||||||
@ -180,7 +186,7 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(final MPUser<?> o) {
|
public int compareTo(@Nonnull final MPUser<?> o) {
|
||||||
int comparison = (o instanceof MPFileUser)? ((MPFileUser) o).getLastUsed().compareTo( getLastUsed() ): 0;
|
int comparison = (o instanceof MPFileUser)? ((MPFileUser) o).getLastUsed().compareTo( getLastUsed() ): 0;
|
||||||
if (comparison != 0)
|
if (comparison != 0)
|
||||||
return comparison;
|
return comparison;
|
||||||
|
@ -79,7 +79,7 @@ public class MPFlatMarshaller implements MPMarshaller {
|
|||||||
site.getAlgorithm().version().toInt(), // algorithm
|
site.getAlgorithm().version().toInt(), // algorithm
|
||||||
site.getCounter().intValue() ), // counter
|
site.getCounter().intValue() ), // counter
|
||||||
ifNotNullElse( loginName, "" ), // loginName
|
ifNotNullElse( loginName, "" ), // loginName
|
||||||
site.getName(), // siteName
|
site.getSiteName(), // siteName
|
||||||
ifNotNullElse( password, "" ) // password
|
ifNotNullElse( password, "" ) // password
|
||||||
) );
|
) );
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ public class MPJSONFile extends MPJSONAnyObject {
|
|||||||
// Clear Text
|
// Clear Text
|
||||||
content = modelSite.getResult();
|
content = modelSite.getResult();
|
||||||
loginContent = modelUser.getMasterKey().siteResult(
|
loginContent = modelUser.getMasterKey().siteResult(
|
||||||
modelSite.getName(), modelSite.getAlgorithm(), modelSite.getAlgorithm().mpw_default_counter(),
|
modelSite.getSiteName(), modelSite.getAlgorithm(), modelSite.getAlgorithm().mpw_default_counter(),
|
||||||
MPKeyPurpose.Identification, null, modelSite.getLoginType(), modelSite.getLoginState() );
|
MPKeyPurpose.Identification, null, modelSite.getLoginType(), modelSite.getLoginState() );
|
||||||
} else {
|
} else {
|
||||||
// Redacted
|
// Redacted
|
||||||
@ -100,9 +100,9 @@ public class MPJSONFile extends MPJSONAnyObject {
|
|||||||
loginContent = modelSite.getLoginState();
|
loginContent = modelSite.getLoginState();
|
||||||
}
|
}
|
||||||
|
|
||||||
Site site = sites.get( modelSite.getName() );
|
Site site = sites.get( modelSite.getSiteName() );
|
||||||
if (site == null)
|
if (site == null)
|
||||||
sites.put( modelSite.getName(), site = new Site() );
|
sites.put( modelSite.getSiteName(), site = new Site() );
|
||||||
site.type = modelSite.getResultType();
|
site.type = modelSite.getResultType();
|
||||||
site.counter = modelSite.getCounter().longValue();
|
site.counter = modelSite.getCounter().longValue();
|
||||||
site.algorithm = modelSite.getAlgorithm().version();
|
site.algorithm = modelSite.getAlgorithm().version();
|
||||||
|
Loading…
Reference in New Issue
Block a user