Commit 1bf61090 authored by Maarten Billemont's avatar Maarten Billemont

Hide passwords option & fix settings for new sites.

parent f2fa2a25
Pipeline #31212874 failed with stage
in 4 minutes and 4 seconds
Subproject commit b713577cd6cf07d516ec2be2220d9dc5f6ee1a4e Subproject commit bc737d41fa15c9e4d05e7f73c25995da67611e52
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> { ...@@ -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 { ...@@ -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.*; ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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> { ...@@ -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> { ...@@ -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> { ...@@ -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> { ...@@ -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 { ...@@ -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; ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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
......
Subproject commit 914a60cd25707f4ac456ad225580a86a5a95e637 Subproject commit d8d510b6be2e2136a040624b8d0ed7590b6e7530
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment