Hide passwords option & fix settings for new sites.
This commit is contained in:
parent
f2fa2a25b2
commit
1bf6109038
2
platform-darwin/External/Pearl
vendored
2
platform-darwin/External/Pearl
vendored
@ -1 +1 @@
|
|||||||
Subproject commit b713577cd6cf07d516ec2be2220d9dc5f6ee1a4e
|
Subproject commit bc737d41fa15c9e4d05e7f73c25995da67611e52
|
@ -1,7 +1,7 @@
|
|||||||
package com.lyndir.masterpassword.gui.model;
|
package com.lyndir.masterpassword.gui.model;
|
||||||
|
|
||||||
import com.lyndir.masterpassword.model.*;
|
import com.lyndir.masterpassword.model.*;
|
||||||
import com.lyndir.masterpassword.model.impl.*;
|
import com.lyndir.masterpassword.model.impl.MPBasicSite;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
|
||||||
@ -19,4 +19,14 @@ public class MPNewSite extends MPBasicSite<MPUser<?>, MPQuestion> {
|
|||||||
public MPQuestion addQuestion(final String keyword) {
|
public MPQuestion addQuestion(final String keyword) {
|
||||||
throw new UnsupportedOperationException( "Cannot add a question to a site that hasn't been created yet." );
|
throw new UnsupportedOperationException( "Cannot add a question to a site that hasn't been created yet." );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <S extends MPSite<?>> S addTo(final MPUser<S> user) {
|
||||||
|
S site = user.addSite( getSiteName() );
|
||||||
|
site.setAlgorithm( getAlgorithm() );
|
||||||
|
site.setCounter( getCounter() );
|
||||||
|
site.setLoginType( getLoginType() );
|
||||||
|
site.setResultType( getResultType() );
|
||||||
|
|
||||||
|
return site;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -426,10 +426,18 @@ public abstract class Components {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static JCheckBox checkBox(final String label) {
|
public static JCheckBox checkBox(final String label) {
|
||||||
|
return checkBox( label, false, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JCheckBox checkBox(final String label, final boolean selected, @Nullable final Consumer<Boolean> selectionConsumer) {
|
||||||
return new JCheckBox( label ) {
|
return new JCheckBox( label ) {
|
||||||
{
|
{
|
||||||
setBackground( null );
|
setBackground( null );
|
||||||
setAlignmentX( LEFT_ALIGNMENT );
|
setAlignmentX( LEFT_ALIGNMENT );
|
||||||
|
setSelected( selected );
|
||||||
|
|
||||||
|
if (selectionConsumer != null)
|
||||||
|
addItemListener( e -> selectionConsumer.accept( isSelected() ) );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ import java.util.*;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
@ -47,6 +48,7 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
|
|||||||
private static final Logger logger = Logger.get( UserContentPanel.class );
|
private static final Logger logger = Logger.get( UserContentPanel.class );
|
||||||
private static final JButton iconButton = Components.button( Res.icons().user(), null, null );
|
private static final JButton iconButton = Components.button( Res.icons().user(), null, null );
|
||||||
private static final KeyStroke copyLoginKeyStroke = KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, InputEvent.SHIFT_DOWN_MASK );
|
private static final KeyStroke copyLoginKeyStroke = KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, InputEvent.SHIFT_DOWN_MASK );
|
||||||
|
private static final Pattern EACH_CHARACTER = Pattern.compile( "." );
|
||||||
|
|
||||||
private final JButton addButton = Components.button( Res.icons().add(), event -> addUser(),
|
private final JButton addButton = Components.button( Res.icons().add(), event -> addUser(),
|
||||||
"Add a new user to Master Password." );
|
"Add a new user to Master Password." );
|
||||||
@ -570,17 +572,20 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
|
|||||||
public void showUserPreferences() {
|
public void showUserPreferences() {
|
||||||
ImmutableList.Builder<Component> components = ImmutableList.builder();
|
ImmutableList.Builder<Component> components = ImmutableList.builder();
|
||||||
|
|
||||||
|
components.add( Components.label( "Default Algorithm:" ),
|
||||||
|
Components.comboBox( MPAlgorithm.Version.values(), MPAlgorithm.Version::name,
|
||||||
|
user.getAlgorithm().version(),
|
||||||
|
version -> user.setAlgorithm( version.getAlgorithm() ) ) );
|
||||||
|
|
||||||
MPFileUser fileUser = (user instanceof MPFileUser)? (MPFileUser) user: null;
|
MPFileUser fileUser = (user instanceof MPFileUser)? (MPFileUser) user: null;
|
||||||
if (fileUser != null)
|
if (fileUser != null) {
|
||||||
components.add( Components.label( "Default Password Type:" ),
|
components.add( Components.label( "Default Password Type:" ),
|
||||||
Components.comboBox( MPResultType.values(), MPResultType::getLongName,
|
Components.comboBox( MPResultType.values(), MPResultType::getLongName,
|
||||||
fileUser.getDefaultType(), fileUser::setDefaultType ),
|
fileUser.getDefaultType(), fileUser::setDefaultType ),
|
||||||
Components.strut() );
|
Components.strut() );
|
||||||
|
|
||||||
components.add( Components.label( "Default Algorithm:" ),
|
components.add( Components.checkBox( "Hide Passwords", fileUser.isHidePasswords(), fileUser::setHidePasswords ) );
|
||||||
Components.comboBox( MPAlgorithm.Version.values(), MPAlgorithm.Version::name,
|
}
|
||||||
user.getAlgorithm().version(),
|
|
||||||
version -> user.setAlgorithm( version.getAlgorithm() ) ) );
|
|
||||||
|
|
||||||
Components.showDialog( this, user.getFullName(), new JOptionPane( Components.panel(
|
Components.showDialog( this, user.getFullName(), new JOptionPane( Components.panel(
|
||||||
BoxLayout.PAGE_AXIS, components.build().toArray( new Component[0] ) ) ) );
|
BoxLayout.PAGE_AXIS, components.build().toArray( new Component[0] ) ) ) );
|
||||||
@ -846,7 +851,7 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
|
|||||||
"New Site", JOptionPane.YES_NO_OPTION ))
|
"New Site", JOptionPane.YES_NO_OPTION ))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
site = user.addSite( site.getSiteName() );
|
site = ((MPNewSite) site).addTo( user );
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean loginResult = (copyLoginKeyStroke.getModifiers() & event.getModifiers()) != 0;
|
boolean loginResult = (copyLoginKeyStroke.getModifiers() & event.getModifiers()) != 0;
|
||||||
@ -878,7 +883,12 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
|
|||||||
else if (showLogin && (site != null))
|
else if (showLogin && (site != null))
|
||||||
resultLabel.setText( (result != null)? strf( "Your login for %s:", site.getSiteName() ): " " );
|
resultLabel.setText( (result != null)? strf( "Your login for %s:", site.getSiteName() ): " " );
|
||||||
|
|
||||||
resultField.setText( (result != null)? result: " " );
|
if ((result == null) || result.isEmpty())
|
||||||
|
resultField.setText( " " );
|
||||||
|
else if (!showLogin && (user instanceof MPFileUser) && ((MPFileUser) user).isHidePasswords())
|
||||||
|
resultField.setText( EACH_CHARACTER.matcher( result ).replaceAll( "•" ) );
|
||||||
|
else
|
||||||
|
resultField.setText( result );
|
||||||
settingsButton.setEnabled( result != null );
|
settingsButton.setEnabled( result != null );
|
||||||
questionsButton.setEnabled( result != null );
|
questionsButton.setEnabled( result != null );
|
||||||
editButton.setEnabled( result != null );
|
editButton.setEnabled( result != null );
|
||||||
@ -1016,8 +1026,14 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
|
|||||||
new LinkedList<>( user.findSites( query ) );
|
new LinkedList<>( user.findSites( query ) );
|
||||||
|
|
||||||
if (!Strings.isNullOrEmpty( queryText ))
|
if (!Strings.isNullOrEmpty( queryText ))
|
||||||
if (siteItems.stream().noneMatch( MPQuery.Result::isExact ))
|
if (siteItems.stream().noneMatch( MPQuery.Result::isExact )) {
|
||||||
siteItems.add( MPQuery.Result.allOf( new MPNewSite( user, query.getQuery() ), query.getQuery() ) );
|
MPQuery.Result<? extends MPSite<?>> selectedItem = sitesModel.getSelectedItem();
|
||||||
|
if ((selectedItem != null) && user.equals( selectedItem.getOption().getUser() ) &&
|
||||||
|
queryText.equals( selectedItem.getOption().getSiteName() ))
|
||||||
|
siteItems.add( selectedItem );
|
||||||
|
else
|
||||||
|
siteItems.add( MPQuery.Result.allOf( new MPNewSite( user, query.getQuery() ), query.getQuery() ) );
|
||||||
|
}
|
||||||
|
|
||||||
Res.ui( () -> sitesModel.set( siteItems ) );
|
Res.ui( () -> sitesModel.set( siteItems ) );
|
||||||
} );
|
} );
|
||||||
|
@ -47,6 +47,7 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
|
|||||||
|
|
||||||
private MPResultType defaultType;
|
private MPResultType defaultType;
|
||||||
private ReadableInstant lastUsed;
|
private ReadableInstant lastUsed;
|
||||||
|
private boolean hidePasswords;
|
||||||
private boolean complete;
|
private boolean complete;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@ -54,7 +55,7 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
|
|||||||
throws IOException, MPMarshalException {
|
throws IOException, MPMarshalException {
|
||||||
for (final MPMarshalFormat format : MPMarshalFormat.values())
|
for (final MPMarshalFormat format : MPMarshalFormat.values())
|
||||||
if (file.getName().endsWith( format.fileSuffix() ))
|
if (file.getName().endsWith( format.fileSuffix() ))
|
||||||
return format.unmarshaller().readUser( file );
|
return format.unmarshaller().readUser( file );
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -64,18 +65,19 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPAlgorithm algorithm, final File path) {
|
public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPAlgorithm algorithm, final File path) {
|
||||||
this( fullName, keyID, algorithm, 0, null, new Instant(),
|
this( fullName, keyID, algorithm, 0, null, new Instant(), false,
|
||||||
MPMarshaller.ContentMode.PROTECTED, MPMarshalFormat.DEFAULT, path );
|
MPMarshaller.ContentMode.PROTECTED, MPMarshalFormat.DEFAULT, path );
|
||||||
}
|
}
|
||||||
|
|
||||||
public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPAlgorithm algorithm,
|
public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPAlgorithm algorithm, final int avatar,
|
||||||
final int avatar, @Nullable final MPResultType defaultType, final ReadableInstant lastUsed,
|
@Nullable final MPResultType defaultType, final ReadableInstant lastUsed, final boolean hidePasswords,
|
||||||
final MPMarshaller.ContentMode contentMode, final MPMarshalFormat format, final File path) {
|
final MPMarshaller.ContentMode contentMode, final MPMarshalFormat format, final File path) {
|
||||||
super( avatar, fullName, algorithm );
|
super( avatar, fullName, algorithm );
|
||||||
|
|
||||||
this.keyID = (keyID != null)? keyID.clone(): null;
|
this.keyID = (keyID != null)? keyID.clone(): null;
|
||||||
this.defaultType = (defaultType != null)? defaultType: algorithm.mpw_default_result_type();
|
this.defaultType = (defaultType != null)? defaultType: algorithm.mpw_default_result_type();
|
||||||
this.lastUsed = lastUsed;
|
this.lastUsed = lastUsed;
|
||||||
|
this.hidePasswords = hidePasswords;
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.format = format;
|
this.format = format;
|
||||||
this.contentMode = contentMode;
|
this.contentMode = contentMode;
|
||||||
@ -157,6 +159,18 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
|
|||||||
setChanged();
|
setChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isHidePasswords() {
|
||||||
|
return hidePasswords;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHidePasswords(final boolean hidePasswords) {
|
||||||
|
if (Objects.equals( this.hidePasswords, hidePasswords ))
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.hidePasswords = hidePasswords;
|
||||||
|
setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
protected boolean isComplete() {
|
protected boolean isComplete() {
|
||||||
return complete;
|
return complete;
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ public class MPFlatUnmarshaller implements MPUnmarshaller {
|
|||||||
else if ((fullName != null) && (keyID != null))
|
else if ((fullName != null) && (keyID != null))
|
||||||
// Ends the header.
|
// Ends the header.
|
||||||
return new MPFileUser( fullName, keyID, MPAlgorithm.Version.fromInt( mpVersion ).getAlgorithm(),
|
return new MPFileUser( fullName, keyID, MPAlgorithm.Version.fromInt( mpVersion ).getAlgorithm(),
|
||||||
avatar, defaultType, new Instant( 0 ),
|
avatar, defaultType, new Instant( 0 ), false,
|
||||||
clearContent? MPMarshaller.ContentMode.VISIBLE: MPMarshaller.ContentMode.PROTECTED,
|
clearContent? MPMarshaller.ContentMode.VISIBLE: MPMarshaller.ContentMode.PROTECTED,
|
||||||
MPMarshalFormat.Flat, file.getParentFile() );
|
MPMarshalFormat.Flat, file.getParentFile() );
|
||||||
}
|
}
|
||||||
|
@ -28,8 +28,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||||||
import com.google.common.primitives.UnsignedInteger;
|
import com.google.common.primitives.UnsignedInteger;
|
||||||
import com.lyndir.lhunath.opal.system.CodeUtils;
|
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||||
import com.lyndir.masterpassword.*;
|
import com.lyndir.masterpassword.*;
|
||||||
import com.lyndir.masterpassword.model.MPModelConstants;
|
|
||||||
import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException;
|
import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException;
|
||||||
|
import com.lyndir.masterpassword.model.MPModelConstants;
|
||||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@ -77,6 +77,7 @@ public class MPJSONFile extends MPJSONAnyObject {
|
|||||||
user.avatar = modelUser.getAvatar();
|
user.avatar = modelUser.getAvatar();
|
||||||
user.full_name = modelUser.getFullName();
|
user.full_name = modelUser.getFullName();
|
||||||
user.last_used = MPModelConstants.dateTimeFormatter.print( modelUser.getLastUsed() );
|
user.last_used = MPModelConstants.dateTimeFormatter.print( modelUser.getLastUsed() );
|
||||||
|
user.hide_passwords = modelUser.isHidePasswords();
|
||||||
user.key_id = modelUser.exportKeyID();
|
user.key_id = modelUser.exportKeyID();
|
||||||
user.algorithm = modelUser.getAlgorithm().version();
|
user.algorithm = modelUser.getAlgorithm().version();
|
||||||
user.default_type = modelUser.getDefaultType();
|
user.default_type = modelUser.getDefaultType();
|
||||||
@ -142,7 +143,7 @@ public class MPJSONFile extends MPJSONAnyObject {
|
|||||||
user.full_name, CodeUtils.decodeHex( user.key_id ), algorithm, user.avatar,
|
user.full_name, CodeUtils.decodeHex( user.key_id ), algorithm, user.avatar,
|
||||||
(user.default_type != null)? user.default_type: algorithm.mpw_default_result_type(),
|
(user.default_type != null)? user.default_type: algorithm.mpw_default_result_type(),
|
||||||
(user.last_used != null)? MPModelConstants.dateTimeFormatter.parseDateTime( user.last_used ): new Instant(),
|
(user.last_used != null)? MPModelConstants.dateTimeFormatter.parseDateTime( user.last_used ): new Instant(),
|
||||||
export.redacted? MPMarshaller.ContentMode.PROTECTED: MPMarshaller.ContentMode.VISIBLE,
|
user.hide_passwords, export.redacted? MPMarshaller.ContentMode.PROTECTED: MPMarshaller.ContentMode.VISIBLE,
|
||||||
MPMarshalFormat.JSON, file.getParentFile()
|
MPMarshalFormat.JSON, file.getParentFile()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -202,9 +203,10 @@ public class MPJSONFile extends MPJSONAnyObject {
|
|||||||
|
|
||||||
public static class User extends MPJSONAnyObject {
|
public static class User extends MPJSONAnyObject {
|
||||||
|
|
||||||
int avatar;
|
int avatar;
|
||||||
String full_name;
|
String full_name;
|
||||||
String last_used;
|
String last_used;
|
||||||
|
boolean hide_passwords;
|
||||||
@Nullable
|
@Nullable
|
||||||
String key_id;
|
String key_id;
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 914a60cd25707f4ac456ad225580a86a5a95e637
|
Subproject commit d8d510b6be2e2136a040624b8d0ed7590b6e7530
|
Loading…
Reference in New Issue
Block a user