Hide passwords option & fix settings for new sites.
This commit is contained in:
		
							
								
								
									
										2
									
								
								platform-darwin/External/Pearl
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								platform-darwin/External/Pearl
									
									
									
									
										vendored
									
									
								
							 Submodule platform-darwin/External/Pearl updated: b713577cd6...bc737d41fa
									
								
							@@ -1,7 +1,7 @@
 | 
			
		||||
package com.lyndir.masterpassword.gui.model;
 | 
			
		||||
 | 
			
		||||
import com.lyndir.masterpassword.model.*;
 | 
			
		||||
import com.lyndir.masterpassword.model.impl.*;
 | 
			
		||||
import com.lyndir.masterpassword.model.impl.MPBasicSite;
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -19,4 +19,14 @@ public class MPNewSite extends MPBasicSite<MPUser<?>, MPQuestion> {
 | 
			
		||||
    public MPQuestion addQuestion(final String keyword) {
 | 
			
		||||
        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) {
 | 
			
		||||
        return checkBox( label, false, null );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static JCheckBox checkBox(final String label, final boolean selected, @Nullable final Consumer<Boolean> selectionConsumer) {
 | 
			
		||||
        return new JCheckBox( label ) {
 | 
			
		||||
            {
 | 
			
		||||
                setBackground( null );
 | 
			
		||||
                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.concurrent.Future;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
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 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 Pattern   EACH_CHARACTER     = Pattern.compile( "." );
 | 
			
		||||
 | 
			
		||||
    private final JButton addButton    = Components.button( Res.icons().add(), event -> addUser(),
 | 
			
		||||
                                                            "Add a new user to Master Password." );
 | 
			
		||||
@@ -570,17 +572,20 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
 | 
			
		||||
        public void showUserPreferences() {
 | 
			
		||||
            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;
 | 
			
		||||
            if (fileUser != null)
 | 
			
		||||
            if (fileUser != null) {
 | 
			
		||||
                components.add( Components.label( "Default Password Type:" ),
 | 
			
		||||
                                Components.comboBox( MPResultType.values(), MPResultType::getLongName,
 | 
			
		||||
                                                     fileUser.getDefaultType(), fileUser::setDefaultType ),
 | 
			
		||||
                                Components.strut() );
 | 
			
		||||
 | 
			
		||||
            components.add( Components.label( "Default Algorithm:" ),
 | 
			
		||||
                            Components.comboBox( MPAlgorithm.Version.values(), MPAlgorithm.Version::name,
 | 
			
		||||
                                                 user.getAlgorithm().version(),
 | 
			
		||||
                                                 version -> user.setAlgorithm( version.getAlgorithm() ) ) );
 | 
			
		||||
                components.add( Components.checkBox( "Hide Passwords", fileUser.isHidePasswords(), fileUser::setHidePasswords ) );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Components.showDialog( this, user.getFullName(), new JOptionPane( Components.panel(
 | 
			
		||||
                    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 ))
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                site = user.addSite( site.getSiteName() );
 | 
			
		||||
                site = ((MPNewSite) site).addTo( user );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            boolean   loginResult = (copyLoginKeyStroke.getModifiers() & event.getModifiers()) != 0;
 | 
			
		||||
@@ -878,7 +883,12 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
 | 
			
		||||
                else if (showLogin && (site != null))
 | 
			
		||||
                    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 );
 | 
			
		||||
                questionsButton.setEnabled( result != null );
 | 
			
		||||
                editButton.setEnabled( result != null );
 | 
			
		||||
@@ -1016,8 +1026,14 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
 | 
			
		||||
                        new LinkedList<>( user.findSites( query ) );
 | 
			
		||||
 | 
			
		||||
                if (!Strings.isNullOrEmpty( queryText ))
 | 
			
		||||
                    if (siteItems.stream().noneMatch( MPQuery.Result::isExact ))
 | 
			
		||||
                        siteItems.add( MPQuery.Result.allOf( new MPNewSite( user, query.getQuery() ), query.getQuery() ) );
 | 
			
		||||
                    if (siteItems.stream().noneMatch( MPQuery.Result::isExact )) {
 | 
			
		||||
                        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 ) );
 | 
			
		||||
            } );
 | 
			
		||||
 
 | 
			
		||||
@@ -47,6 +47,7 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
 | 
			
		||||
 | 
			
		||||
    private MPResultType    defaultType;
 | 
			
		||||
    private ReadableInstant lastUsed;
 | 
			
		||||
    private boolean         hidePasswords;
 | 
			
		||||
    private boolean         complete;
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
@@ -54,7 +55,7 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
 | 
			
		||||
            throws IOException, MPMarshalException {
 | 
			
		||||
        for (final MPMarshalFormat format : MPMarshalFormat.values())
 | 
			
		||||
            if (file.getName().endsWith( format.fileSuffix() ))
 | 
			
		||||
                    return format.unmarshaller().readUser( file );
 | 
			
		||||
                return format.unmarshaller().readUser( file );
 | 
			
		||||
 | 
			
		||||
        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) {
 | 
			
		||||
        this( fullName, keyID, algorithm, 0, null, new Instant(),
 | 
			
		||||
        this( fullName, keyID, algorithm, 0, null, new Instant(), false,
 | 
			
		||||
              MPMarshaller.ContentMode.PROTECTED, MPMarshalFormat.DEFAULT, path );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPAlgorithm algorithm,
 | 
			
		||||
                      final int avatar, @Nullable final MPResultType defaultType, final ReadableInstant lastUsed,
 | 
			
		||||
    public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPAlgorithm algorithm, final int avatar,
 | 
			
		||||
                      @Nullable final MPResultType defaultType, final ReadableInstant lastUsed, final boolean hidePasswords,
 | 
			
		||||
                      final MPMarshaller.ContentMode contentMode, final MPMarshalFormat format, final File path) {
 | 
			
		||||
        super( avatar, fullName, algorithm );
 | 
			
		||||
 | 
			
		||||
        this.keyID = (keyID != null)? keyID.clone(): null;
 | 
			
		||||
        this.defaultType = (defaultType != null)? defaultType: algorithm.mpw_default_result_type();
 | 
			
		||||
        this.lastUsed = lastUsed;
 | 
			
		||||
        this.hidePasswords = hidePasswords;
 | 
			
		||||
        this.path = path;
 | 
			
		||||
        this.format = format;
 | 
			
		||||
        this.contentMode = contentMode;
 | 
			
		||||
@@ -157,6 +159,18 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
 | 
			
		||||
        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() {
 | 
			
		||||
        return complete;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -67,7 +67,7 @@ public class MPFlatUnmarshaller implements MPUnmarshaller {
 | 
			
		||||
                    else if ((fullName != null) && (keyID != null))
 | 
			
		||||
                        // Ends the header.
 | 
			
		||||
                        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,
 | 
			
		||||
                                               MPMarshalFormat.Flat, file.getParentFile() );
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -28,8 +28,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 | 
			
		||||
import com.google.common.primitives.UnsignedInteger;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.CodeUtils;
 | 
			
		||||
import com.lyndir.masterpassword.*;
 | 
			
		||||
import com.lyndir.masterpassword.model.MPModelConstants;
 | 
			
		||||
import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException;
 | 
			
		||||
import com.lyndir.masterpassword.model.MPModelConstants;
 | 
			
		||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.util.LinkedHashMap;
 | 
			
		||||
@@ -77,6 +77,7 @@ public class MPJSONFile extends MPJSONAnyObject {
 | 
			
		||||
        user.avatar = modelUser.getAvatar();
 | 
			
		||||
        user.full_name = modelUser.getFullName();
 | 
			
		||||
        user.last_used = MPModelConstants.dateTimeFormatter.print( modelUser.getLastUsed() );
 | 
			
		||||
        user.hide_passwords = modelUser.isHidePasswords();
 | 
			
		||||
        user.key_id = modelUser.exportKeyID();
 | 
			
		||||
        user.algorithm = modelUser.getAlgorithm().version();
 | 
			
		||||
        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.default_type != null)? user.default_type: algorithm.mpw_default_result_type(),
 | 
			
		||||
                (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()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
@@ -202,9 +203,10 @@ public class MPJSONFile extends MPJSONAnyObject {
 | 
			
		||||
 | 
			
		||||
    public static class User extends MPJSONAnyObject {
 | 
			
		||||
 | 
			
		||||
        int    avatar;
 | 
			
		||||
        String full_name;
 | 
			
		||||
        String last_used;
 | 
			
		||||
        int     avatar;
 | 
			
		||||
        String  full_name;
 | 
			
		||||
        String  last_used;
 | 
			
		||||
        boolean hide_passwords;
 | 
			
		||||
        @Nullable
 | 
			
		||||
        String              key_id;
 | 
			
		||||
        @Nullable
 | 
			
		||||
 
 | 
			
		||||
 Submodule public/site updated: 914a60cd25...d8d510b6be
									
								
							
		Reference in New Issue
	
	Block a user