User config in _ext_mpw, global config.json & residence config.
Moved user preferences (default type & hide passwords) into _ext_mpw. Fixed an issue with JSON serialization of any values. Made update check & background residency globally configurable preferences saved in config.json.
This commit is contained in:
parent
34042e5462
commit
39dacc8e5a
@ -19,6 +19,7 @@
|
|||||||
package com.lyndir.masterpassword.gui;
|
package com.lyndir.masterpassword.gui;
|
||||||
|
|
||||||
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
|
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
|
||||||
|
import com.lyndir.masterpassword.model.MPConfig;
|
||||||
import com.lyndir.masterpassword.model.MPModelConstants;
|
import com.lyndir.masterpassword.model.MPModelConstants;
|
||||||
|
|
||||||
|
|
||||||
@ -26,15 +27,32 @@ import com.lyndir.masterpassword.model.MPModelConstants;
|
|||||||
* @author lhunath, 2014-08-31
|
* @author lhunath, 2014-08-31
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("CallToSystemGetenv")
|
@SuppressWarnings("CallToSystemGetenv")
|
||||||
public class MPConfig {
|
public class MPGuiConfig extends MPConfig {
|
||||||
|
|
||||||
private static final MPConfig instance = new MPConfig();
|
public static MPGuiConfig get() {
|
||||||
|
return get( MPGuiConfig.class );
|
||||||
public static MPConfig get() {
|
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Boolean checkForUpdates;
|
||||||
|
Boolean stayResident;
|
||||||
|
|
||||||
public boolean checkForUpdates() {
|
public boolean checkForUpdates() {
|
||||||
return ConversionUtils.toBoolean( System.getenv( MPModelConstants.env_checkUpdates ) ).orElse( true );
|
return (checkForUpdates != null)? checkForUpdates:
|
||||||
|
ConversionUtils.toBoolean( System.getenv( MPModelConstants.env_checkUpdates ) ).orElse( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCheckForUpdates(final boolean checkForUpdates) {
|
||||||
|
this.checkForUpdates = checkForUpdates;
|
||||||
|
MasterPassword.get().updateCheck();
|
||||||
|
setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean stayResident() {
|
||||||
|
return (stayResident != null)? stayResident: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStayResident(final boolean stayResident) {
|
||||||
|
this.stayResident = stayResident;
|
||||||
|
setChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -47,9 +47,9 @@ public final class MasterPassword {
|
|||||||
|
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
private static final Logger logger = Logger.get( MasterPassword.class );
|
private static final Logger logger = Logger.get( MasterPassword.class );
|
||||||
|
|
||||||
private static final MasterPassword instance = new MasterPassword();
|
private static final MasterPassword instance = new MasterPassword();
|
||||||
|
|
||||||
|
private final Provider keyMaster = Provider.getCurrentProvider( true );
|
||||||
private final Collection<Listener> listeners = new CopyOnWriteArraySet<>();
|
private final Collection<Listener> listeners = new CopyOnWriteArraySet<>();
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@ -97,7 +97,29 @@ public final class MasterPassword {
|
|||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkUpdate() {
|
public static void main(final String... args) {
|
||||||
|
//Thread.setDefaultUncaughtExceptionHandler(
|
||||||
|
// (t, e) -> logger.bug( e, "Uncaught: %s", e.getLocalizedMessage() ) );
|
||||||
|
|
||||||
|
// Set the system look & feel, if available.
|
||||||
|
try {
|
||||||
|
UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
|
||||||
|
}
|
||||||
|
catch (final UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and open the UI.
|
||||||
|
get().open();
|
||||||
|
|
||||||
|
// UI features.
|
||||||
|
get().updateResidence();
|
||||||
|
get().updateCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateCheck() {
|
||||||
|
if (!MPGuiConfig.get().checkForUpdates())
|
||||||
|
return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String implementationVersion = version();
|
String implementationVersion = version();
|
||||||
String latestVersion = new ByteSource() {
|
String latestVersion = new ByteSource() {
|
||||||
@ -127,26 +149,10 @@ public final class MasterPassword {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(final String... args) {
|
public void updateResidence() {
|
||||||
//Thread.setDefaultUncaughtExceptionHandler(
|
|
||||||
// (t, e) -> logger.bug( e, "Uncaught: %s", e.getLocalizedMessage() ) );
|
|
||||||
|
|
||||||
// Try and set the system look & feel, if available.
|
|
||||||
try {
|
|
||||||
UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
|
|
||||||
Platform.get().installAppForegroundHandler( get()::open );
|
Platform.get().installAppForegroundHandler( get()::open );
|
||||||
Platform.get().installAppReopenHandler( get()::open );
|
Platform.get().installAppReopenHandler( get()::open );
|
||||||
Provider.getCurrentProvider( true ).register( MPGuiConstants.ui_hotkey, hotKey -> get().open() );
|
keyMaster.register( MPGuiConstants.ui_hotkey, hotKey -> get().open() );
|
||||||
}
|
|
||||||
catch (final UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException ignored) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a platform-specific GUI and open it.
|
|
||||||
get().open();
|
|
||||||
|
|
||||||
// Check online to see if this version has been superseded.
|
|
||||||
if (MPConfig.get().checkForUpdates())
|
|
||||||
get().checkUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("InterfaceMayBeAnnotatedFunctional")
|
@SuppressWarnings("InterfaceMayBeAnnotatedFunctional")
|
||||||
|
@ -17,9 +17,13 @@ public class ApplePlatform implements IPlatform {
|
|||||||
private static final Application application = Preconditions.checkNotNull(
|
private static final Application application = Preconditions.checkNotNull(
|
||||||
Application.getApplication(), "Not an Apple Java application." );
|
Application.getApplication(), "Not an Apple Java application." );
|
||||||
|
|
||||||
|
private AppForegroundListener appForegroundHandler;
|
||||||
|
private AppReOpenedListener appReopenHandler;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean installAppForegroundHandler(final Runnable handler) {
|
public boolean installAppForegroundHandler(final Runnable handler) {
|
||||||
application.addAppEventListener( new AppForegroundListener() {
|
if (appForegroundHandler == null)
|
||||||
|
application.addAppEventListener( appForegroundHandler = new AppForegroundListener() {
|
||||||
@Override
|
@Override
|
||||||
public void appMovedToBackground(final AppEvent.AppForegroundEvent e) {
|
public void appMovedToBackground(final AppEvent.AppForegroundEvent e) {
|
||||||
}
|
}
|
||||||
@ -29,12 +33,31 @@ public class ApplePlatform implements IPlatform {
|
|||||||
handler.run();
|
handler.run();
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeAppForegroundHandler() {
|
||||||
|
if (appForegroundHandler == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
application.removeAppEventListener( appForegroundHandler );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean installAppReopenHandler(final Runnable handler) {
|
public boolean installAppReopenHandler(final Runnable handler) {
|
||||||
application.addAppEventListener( (AppReOpenedListener) e -> handler.run() );
|
application.addAppEventListener( appReopenHandler = e -> handler.run() );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeAppReopenHandler() {
|
||||||
|
if (appReopenHandler == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
application.removeAppEventListener( appReopenHandler );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,11 +14,21 @@ public class BasePlatform implements IPlatform {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeAppForegroundHandler() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean installAppReopenHandler(final Runnable handler) {
|
public boolean installAppReopenHandler(final Runnable handler) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeAppReopenHandler() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean requestForeground() {
|
public boolean requestForeground() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -12,8 +12,12 @@ public interface IPlatform {
|
|||||||
|
|
||||||
boolean installAppForegroundHandler(Runnable handler);
|
boolean installAppForegroundHandler(Runnable handler);
|
||||||
|
|
||||||
|
boolean removeAppForegroundHandler();
|
||||||
|
|
||||||
boolean installAppReopenHandler(Runnable handler);
|
boolean installAppReopenHandler(Runnable handler);
|
||||||
|
|
||||||
|
boolean removeAppReopenHandler();
|
||||||
|
|
||||||
boolean requestForeground();
|
boolean requestForeground();
|
||||||
|
|
||||||
boolean show(File file);
|
boolean show(File file);
|
||||||
|
@ -17,9 +17,13 @@ public class JDK9Platform implements IPlatform {
|
|||||||
private static final Logger logger = Logger.get( JDK9Platform.class );
|
private static final Logger logger = Logger.get( JDK9Platform.class );
|
||||||
private static final Desktop desktop = Desktop.getDesktop();
|
private static final Desktop desktop = Desktop.getDesktop();
|
||||||
|
|
||||||
|
private AppForegroundListener appForegroundHandler;
|
||||||
|
private AppReopenedListener appReopenHandler;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean installAppForegroundHandler(final Runnable handler) {
|
public boolean installAppForegroundHandler(final Runnable handler) {
|
||||||
desktop.addAppEventListener( new AppForegroundListener() {
|
if (appForegroundHandler == null)
|
||||||
|
desktop.addAppEventListener( appForegroundHandler = new AppForegroundListener() {
|
||||||
@Override
|
@Override
|
||||||
public void appRaisedToForeground(final AppForegroundEvent e) {
|
public void appRaisedToForeground(final AppForegroundEvent e) {
|
||||||
handler.run();
|
handler.run();
|
||||||
@ -29,12 +33,33 @@ public class JDK9Platform implements IPlatform {
|
|||||||
public void appMovedToBackground(final AppForegroundEvent e) {
|
public void appMovedToBackground(final AppForegroundEvent e) {
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeAppForegroundHandler() {
|
||||||
|
if (appForegroundHandler == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
desktop.removeAppEventListener( appForegroundHandler );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean installAppReopenHandler(final Runnable handler) {
|
public boolean installAppReopenHandler(final Runnable handler) {
|
||||||
desktop.addAppEventListener( (AppReopenedListener) e -> handler.run() );
|
if (appReopenHandler == null)
|
||||||
|
desktop.addAppEventListener( appReopenHandler = e -> handler.run() );
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeAppReopenHandler() {
|
||||||
|
if (appReopenHandler == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
desktop.removeAppEventListener( appReopenHandler );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package com.lyndir.masterpassword.gui.view;
|
package com.lyndir.masterpassword.gui.view;
|
||||||
|
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
|
import com.lyndir.masterpassword.gui.MPGuiConfig;
|
||||||
import com.lyndir.masterpassword.gui.util.Components;
|
import com.lyndir.masterpassword.gui.util.Components;
|
||||||
import com.lyndir.masterpassword.gui.util.Res;
|
import com.lyndir.masterpassword.gui.util.Res;
|
||||||
import com.lyndir.masterpassword.model.impl.MPFileUserManager;
|
import com.lyndir.masterpassword.model.impl.MPFileUserManager;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.ComponentAdapter;
|
import java.awt.event.*;
|
||||||
import java.awt.event.ComponentEvent;
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.BevelBorder;
|
import javax.swing.border.BevelBorder;
|
||||||
|
|
||||||
@ -39,6 +39,7 @@ public class MasterPasswordFrame extends JFrame {
|
|||||||
userPanel.add( userContent.getSiteToolbar(), BorderLayout.LINE_END );
|
userPanel.add( userContent.getSiteToolbar(), BorderLayout.LINE_END );
|
||||||
|
|
||||||
addComponentListener( new ComponentHandler() );
|
addComponentListener( new ComponentHandler() );
|
||||||
|
addWindowListener( new WindowHandler() );
|
||||||
setPreferredSize( new Dimension( 800, 560 ) );
|
setPreferredSize( new Dimension( 800, 560 ) );
|
||||||
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
|
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
|
||||||
pack();
|
pack();
|
||||||
@ -55,4 +56,14 @@ public class MasterPasswordFrame extends JFrame {
|
|||||||
userContent.transferFocus();
|
userContent.transferFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class WindowHandler extends WindowAdapter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void windowClosed(final WindowEvent e) {
|
||||||
|
if (!MPGuiConfig.get().stayResident())
|
||||||
|
System.exit( 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,7 @@ import com.google.common.util.concurrent.ListenableFuture;
|
|||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
import com.lyndir.lhunath.opal.system.util.ObjectUtils;
|
import com.lyndir.lhunath.opal.system.util.ObjectUtils;
|
||||||
import com.lyndir.masterpassword.*;
|
import com.lyndir.masterpassword.*;
|
||||||
import com.lyndir.masterpassword.gui.MPGuiConstants;
|
import com.lyndir.masterpassword.gui.*;
|
||||||
import com.lyndir.masterpassword.gui.MasterPassword;
|
|
||||||
import com.lyndir.masterpassword.gui.model.*;
|
import com.lyndir.masterpassword.gui.model.*;
|
||||||
import com.lyndir.masterpassword.gui.util.*;
|
import com.lyndir.masterpassword.gui.util.*;
|
||||||
import com.lyndir.masterpassword.gui.util.Platform;
|
import com.lyndir.masterpassword.gui.util.Platform;
|
||||||
@ -577,21 +576,25 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
|
|||||||
|
|
||||||
components.add( Components.label( "Default Algorithm:" ),
|
components.add( Components.label( "Default Algorithm:" ),
|
||||||
Components.comboBox( MPAlgorithm.Version.values(), MPAlgorithm.Version::name,
|
Components.comboBox( MPAlgorithm.Version.values(), MPAlgorithm.Version::name,
|
||||||
user.getAlgorithm().version(),
|
user.getAlgorithm().version(), version -> user.setAlgorithm( version.getAlgorithm() ) ) );
|
||||||
version -> user.setAlgorithm( version.getAlgorithm() ) ) );
|
|
||||||
|
|
||||||
MPFileUser fileUser = (user instanceof MPFileUser)? (MPFileUser) user: 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.getPreferences().getDefaultType(),
|
user.getPreferences().getDefaultType(), user.getPreferences()::setDefaultType ),
|
||||||
fileUser.getPreferences()::setDefaultType ),
|
|
||||||
Components.strut() );
|
Components.strut() );
|
||||||
|
|
||||||
components.add( Components.checkBox( "Hide Passwords",
|
components.add( Components.checkBox( "Hide Passwords",
|
||||||
fileUser.getPreferences().isHidePasswords(),
|
user.getPreferences().isHidePasswords(), user.getPreferences()::setHidePasswords ) );
|
||||||
fileUser.getPreferences()::setHidePasswords ) );
|
|
||||||
}
|
components.add( new JSeparator() );
|
||||||
|
|
||||||
|
components.add( Components.checkBox( "Check For Updates",
|
||||||
|
MPGuiConfig.get().checkForUpdates(), MPGuiConfig.get()::setCheckForUpdates ) );
|
||||||
|
|
||||||
|
components.add( Components.checkBox( strf( "<html>Stay Resident (reactivate with <strong><code>%s+%s</code></strong>)",
|
||||||
|
InputEvent.getModifiersExText( MPGuiConstants.ui_hotkey.getModifiers() ),
|
||||||
|
KeyEvent.getKeyText( MPGuiConstants.ui_hotkey.getKeyCode() ) ),
|
||||||
|
MPGuiConfig.get().stayResident(), MPGuiConfig.get()::setStayResident ) );
|
||||||
|
|
||||||
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] ) ) ) );
|
||||||
|
@ -0,0 +1,78 @@
|
|||||||
|
package com.lyndir.masterpassword.model;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.google.common.collect.ClassToInstanceMap;
|
||||||
|
import com.google.common.collect.MutableClassToInstanceMap;
|
||||||
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
|
import com.lyndir.masterpassword.model.impl.Changeable;
|
||||||
|
import com.lyndir.masterpassword.model.impl.MPJSONAnyObject;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lhunath, 2018-10-14
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("CallToSystemGetenv")
|
||||||
|
public class MPConfig extends MPJSONAnyObject {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.get( MPConfig.class );
|
||||||
|
private static final ClassToInstanceMap<MPConfig> instances = MutableClassToInstanceMap.create();
|
||||||
|
private static final File configFile = new File( rcDir(), "config.json" );
|
||||||
|
|
||||||
|
private final Changeable changeable = new Changeable() {
|
||||||
|
@Override
|
||||||
|
protected void onChanged() {
|
||||||
|
try {
|
||||||
|
objectMapper.writerWithDefaultPrettyPrinter().writeValue( configFile, MPConfig.this );
|
||||||
|
instances.clear();
|
||||||
|
}
|
||||||
|
catch (final IOException e) {
|
||||||
|
logger.err( e, "While saving config to: %s", configFile );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
protected static synchronized <C extends MPConfig> C get(final Class<C> type) {
|
||||||
|
C instance = instances.getInstance( type );
|
||||||
|
|
||||||
|
if (instance == null)
|
||||||
|
if (configFile.exists())
|
||||||
|
try {
|
||||||
|
instances.putInstance( type, instance = objectMapper.readValue( configFile, type ) );
|
||||||
|
}
|
||||||
|
catch (final IOException e) {
|
||||||
|
logger.wrn( e, "While reading config file: %s", configFile );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instance == null)
|
||||||
|
try {
|
||||||
|
instance = type.getConstructor().newInstance();
|
||||||
|
}
|
||||||
|
catch (final ReflectiveOperationException e) {
|
||||||
|
throw logger.bug( e );
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setChanged() {
|
||||||
|
changeable.setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MPConfig get() {
|
||||||
|
return get( MPConfig.class );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File rcDir() {
|
||||||
|
String rcDir = System.getenv( MPModelConstants.env_rcDir );
|
||||||
|
if (rcDir != null)
|
||||||
|
return new File( rcDir );
|
||||||
|
|
||||||
|
String home = System.getProperty( "user.home" );
|
||||||
|
if (home == null)
|
||||||
|
home = System.getenv( "HOME" );
|
||||||
|
|
||||||
|
return new File( home, ".mpw.d" );
|
||||||
|
}
|
||||||
|
}
|
@ -40,14 +40,14 @@ public interface MPQuestion extends Comparable<MPQuestion> {
|
|||||||
|
|
||||||
void setType(MPResultType type);
|
void setType(MPResultType type);
|
||||||
|
|
||||||
@Nonnull
|
@Nullable
|
||||||
default String getAnswer()
|
default String getAnswer()
|
||||||
throws MPKeyUnavailableException, MPAlgorithmException {
|
throws MPKeyUnavailableException, MPAlgorithmException {
|
||||||
|
|
||||||
return getAnswer( null );
|
return getAnswer( null );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nullable
|
||||||
String getAnswer(@Nullable String state)
|
String getAnswer(@Nullable String state)
|
||||||
throws MPKeyUnavailableException, MPAlgorithmException;
|
throws MPKeyUnavailableException, MPAlgorithmException;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import java.util.concurrent.Executors;
|
|||||||
/**
|
/**
|
||||||
* @author lhunath, 2018-07-08
|
* @author lhunath, 2018-07-08
|
||||||
*/
|
*/
|
||||||
public class Changeable {
|
public abstract class Changeable {
|
||||||
|
|
||||||
private static final ExecutorService changeExecutor = Executors.newSingleThreadExecutor();
|
private static final ExecutorService changeExecutor = Executors.newSingleThreadExecutor();
|
||||||
|
|
||||||
@ -15,7 +15,9 @@ public class Changeable {
|
|||||||
private Grouping grouping = Grouping.APPLY;
|
private Grouping grouping = Grouping.APPLY;
|
||||||
private boolean changed;
|
private boolean changed;
|
||||||
|
|
||||||
void setChanged() {
|
protected abstract void onChanged();
|
||||||
|
|
||||||
|
public void setChanged() {
|
||||||
synchronized (mutex) {
|
synchronized (mutex) {
|
||||||
if (changed)
|
if (changed)
|
||||||
return;
|
return;
|
||||||
@ -37,9 +39,6 @@ public class Changeable {
|
|||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onChanged() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void beginChanges() {
|
public void beginChanges() {
|
||||||
synchronized (mutex) {
|
synchronized (mutex) {
|
||||||
grouping = Grouping.BATCH;
|
grouping = Grouping.BATCH;
|
||||||
|
@ -72,7 +72,7 @@ public abstract class MPBasicQuestion extends Changeable implements MPQuestion {
|
|||||||
setChanged();
|
setChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public String getAnswer(@Nullable final String state)
|
public String getAnswer(@Nullable final String state)
|
||||||
throws MPKeyUnavailableException, MPAlgorithmException {
|
throws MPKeyUnavailableException, MPAlgorithmException {
|
||||||
@ -82,8 +82,6 @@ public abstract class MPBasicQuestion extends Changeable implements MPQuestion {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onChanged() {
|
protected void onChanged() {
|
||||||
super.onChanged();
|
|
||||||
|
|
||||||
if (site instanceof Changeable)
|
if (site instanceof Changeable)
|
||||||
((Changeable) site).setChanged();
|
((Changeable) site).setChanged();
|
||||||
}
|
}
|
||||||
|
@ -201,8 +201,6 @@ public abstract class MPBasicSite<U extends MPUser<?>, Q extends MPQuestion> ext
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onChanged() {
|
protected void onChanged() {
|
||||||
super.onChanged();
|
|
||||||
|
|
||||||
if (user instanceof Changeable)
|
if (user instanceof Changeable)
|
||||||
((Changeable) user).setChanged();
|
((Changeable) user).setChanged();
|
||||||
}
|
}
|
||||||
|
@ -214,8 +214,6 @@ public abstract class MPBasicUser<S extends MPBasicSite<?, ?>> extends Changeabl
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onChanged() {
|
protected void onChanged() {
|
||||||
super.onChanged();
|
|
||||||
|
|
||||||
for (final Listener listener : listeners)
|
for (final Listener listener : listeners)
|
||||||
listener.onUserUpdated( this );
|
listener.onUserUpdated( this );
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ package com.lyndir.masterpassword.model.impl;
|
|||||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
|
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
|
||||||
|
|
||||||
import com.lyndir.masterpassword.*;
|
import com.lyndir.masterpassword.*;
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
|
||||||
@ -33,6 +32,7 @@ public class MPFileQuestion extends MPBasicQuestion {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private String answerState;
|
private String answerState;
|
||||||
|
|
||||||
|
@SuppressWarnings("TypeMayBeWeakened")
|
||||||
public MPFileQuestion(final MPFileSite site, final String keyword,
|
public MPFileQuestion(final MPFileSite site, final String keyword,
|
||||||
@Nullable final MPResultType type, @Nullable final String answerState) {
|
@Nullable final MPResultType type, @Nullable final String answerState) {
|
||||||
super( site, keyword, ifNotNullElse( type, site.getAlgorithm().mpw_default_answer_type() ) );
|
super( site, keyword, ifNotNullElse( type, site.getAlgorithm().mpw_default_answer_type() ) );
|
||||||
@ -45,7 +45,7 @@ public class MPFileQuestion extends MPBasicQuestion {
|
|||||||
return answerState;
|
return answerState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public String getAnswer()
|
public String getAnswer()
|
||||||
throws MPKeyUnavailableException, MPAlgorithmException {
|
throws MPKeyUnavailableException, MPAlgorithmException {
|
||||||
|
@ -18,11 +18,9 @@
|
|||||||
|
|
||||||
package com.lyndir.masterpassword.model.impl;
|
package com.lyndir.masterpassword.model.impl;
|
||||||
|
|
||||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSortedSet;
|
import com.google.common.collect.ImmutableSortedSet;
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
import com.lyndir.masterpassword.model.MPModelConstants;
|
import com.lyndir.masterpassword.model.MPConfig;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -39,18 +37,7 @@ public class MPFileUserManager {
|
|||||||
|
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
private static final Logger logger = Logger.get( MPFileUserManager.class );
|
private static final Logger logger = Logger.get( MPFileUserManager.class );
|
||||||
private static final MPFileUserManager instance;
|
private static final MPFileUserManager instance = create( MPConfig.get().rcDir() );
|
||||||
|
|
||||||
static {
|
|
||||||
String rcDir = System.getenv( MPModelConstants.env_rcDir );
|
|
||||||
|
|
||||||
if (rcDir != null)
|
|
||||||
instance = create( new File( rcDir ) );
|
|
||||||
else {
|
|
||||||
String home = ifNotNullElseNullable( System.getProperty( "user.home" ), System.getenv( "HOME" ) );
|
|
||||||
instance = create( new File( home, ".mpw.d" ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Collection<Listener> listeners = new CopyOnWriteArraySet<>();
|
private final Collection<Listener> listeners = new CopyOnWriteArraySet<>();
|
||||||
private final Map<String, MPFileUser> userByName = new HashMap<>();
|
private final Map<String, MPFileUser> userByName = new HashMap<>();
|
||||||
|
@ -19,6 +19,9 @@
|
|||||||
package com.lyndir.masterpassword.model.impl;
|
package com.lyndir.masterpassword.model.impl;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.*;
|
import com.fasterxml.jackson.annotation.*;
|
||||||
|
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
|
||||||
|
import com.fasterxml.jackson.core.util.Separators;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@ -27,31 +30,59 @@ import java.util.*;
|
|||||||
* @author lhunath, 2018-05-14
|
* @author lhunath, 2018-05-14
|
||||||
*/
|
*/
|
||||||
@JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = MPJSONAnyObject.MPJSONEmptyValue.class)
|
@JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = MPJSONAnyObject.MPJSONEmptyValue.class)
|
||||||
class MPJSONAnyObject {
|
public class MPJSONAnyObject {
|
||||||
|
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
protected static final ObjectMapper objectMapper = new ObjectMapper() {
|
||||||
|
{
|
||||||
|
setDefaultPrettyPrinter( new DefaultPrettyPrinter() {
|
||||||
|
@Override
|
||||||
|
public DefaultPrettyPrinter withSeparators(final Separators separators) {
|
||||||
|
super.withSeparators( separators );
|
||||||
|
_objectFieldValueSeparatorWithSpaces = separators.getObjectFieldValueSeparator() + " ";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
setVisibility( PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE );
|
||||||
|
setVisibility( PropertyAccessor.FIELD, JsonAutoDetect.Visibility.NON_PRIVATE );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@JsonAnySetter
|
@JsonAnySetter
|
||||||
final Map<String, Object> any = new LinkedHashMap<>();
|
final Map<String, Object> any = new LinkedHashMap<>();
|
||||||
|
|
||||||
@JsonAnyGetter
|
@JsonAnyGetter
|
||||||
public Map<String, Object> getAny() {
|
public Map<String, Object> any() {
|
||||||
return Collections.unmodifiableMap( any );
|
return Collections.unmodifiableMap( any );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <V> V any(final String key) {
|
||||||
|
return (V) any.get( key );
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("EqualsAndHashcode")
|
@SuppressWarnings("EqualsAndHashcode")
|
||||||
public static class MPJSONEmptyValue {
|
public static class MPJSONEmptyValue {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings({ "ChainOfInstanceofChecks", "Contract" })
|
@SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
|
||||||
@SuppressFBWarnings({ "EQ_UNUSUAL", "EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS", "HE_EQUALS_USE_HASHCODE" })
|
@SuppressFBWarnings({ "EQ_UNUSUAL", "EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS", "HE_EQUALS_USE_HASHCODE" })
|
||||||
public boolean equals(final Object obj) {
|
public boolean equals(final Object obj) {
|
||||||
|
return isEmpty( obj );
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "ChainOfInstanceofChecks", "ConstantConditions" })
|
||||||
|
private static boolean isEmpty(final Object obj) {
|
||||||
|
if (obj == null)
|
||||||
|
return true;
|
||||||
if (obj instanceof Collection<?>)
|
if (obj instanceof Collection<?>)
|
||||||
return ((Collection<?>) obj).isEmpty();
|
return ((Collection<?>) obj).isEmpty();
|
||||||
if (obj instanceof Map<?, ?>)
|
if (obj instanceof Map<?, ?>)
|
||||||
return ((Map<?, ?>) obj).isEmpty();
|
return ((Map<?, ?>) obj).isEmpty();
|
||||||
if (obj instanceof MPJSONFile.Site.Ext)
|
if (obj instanceof MPJSONAnyObject)
|
||||||
return ((MPJSONAnyObject) obj).any.isEmpty();
|
return ((MPJSONAnyObject) obj).any.isEmpty() && (objectMapper.valueToTree( obj ).size() == 0);
|
||||||
|
|
||||||
return obj == null;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,22 +44,6 @@ import org.joda.time.Instant;
|
|||||||
@SuppressFBWarnings("URF_UNREAD_FIELD")
|
@SuppressFBWarnings("URF_UNREAD_FIELD")
|
||||||
public class MPJSONFile extends MPJSONAnyObject {
|
public class MPJSONFile extends MPJSONAnyObject {
|
||||||
|
|
||||||
protected static final ObjectMapper objectMapper = new ObjectMapper();
|
|
||||||
|
|
||||||
static {
|
|
||||||
objectMapper.setDefaultPrettyPrinter( new DefaultPrettyPrinter() {
|
|
||||||
private static final long serialVersionUID = 1;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DefaultPrettyPrinter withSeparators(final Separators separators) {
|
|
||||||
super.withSeparators( separators );
|
|
||||||
_objectFieldValueSeparatorWithSpaces = separators.getObjectFieldValueSeparator() + " ";
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
objectMapper.setVisibility( PropertyAccessor.FIELD, JsonAutoDetect.Visibility.NON_PRIVATE );
|
|
||||||
}
|
|
||||||
|
|
||||||
MPJSONFile() {
|
MPJSONFile() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,8 +63,12 @@ public class MPJSONFile extends MPJSONAnyObject {
|
|||||||
user.last_used = MPModelConstants.dateTimeFormatter.print( modelUser.getLastUsed() );
|
user.last_used = MPModelConstants.dateTimeFormatter.print( modelUser.getLastUsed() );
|
||||||
user.key_id = modelUser.exportKeyID();
|
user.key_id = modelUser.exportKeyID();
|
||||||
user.algorithm = modelUser.getAlgorithm().version();
|
user.algorithm = modelUser.getAlgorithm().version();
|
||||||
user.default_type = modelUser.getPreferences().getDefaultType();
|
user._ext_mpw = new User.Ext() {
|
||||||
user.hide_passwords = modelUser.getPreferences().isHidePasswords();
|
{
|
||||||
|
default_type = modelUser.getPreferences().getDefaultType();
|
||||||
|
hide_passwords = modelUser.getPreferences().isHidePasswords();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Section "sites"
|
// Section "sites"
|
||||||
sites = new LinkedHashMap<>();
|
sites = new LinkedHashMap<>();
|
||||||
@ -131,8 +119,11 @@ public class MPJSONFile extends MPJSONAnyObject {
|
|||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
site._ext_mpw = new Site.Ext();
|
site._ext_mpw = new Site.Ext() {
|
||||||
site._ext_mpw.url = modelSite.getUrl();
|
{
|
||||||
|
url = modelSite.getUrl();
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,9 +132,10 @@ public class MPJSONFile extends MPJSONAnyObject {
|
|||||||
|
|
||||||
return new MPFileUser(
|
return new MPFileUser(
|
||||||
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._ext_mpw != null)? user._ext_mpw.default_type: null,
|
||||||
(user.last_used != null)? MPModelConstants.dateTimeFormatter.parseDateTime( user.last_used ): new Instant(),
|
(user.last_used != null)? MPModelConstants.dateTimeFormatter.parseDateTime( user.last_used ): new Instant(),
|
||||||
user.hide_passwords, export.redacted? MPMarshaller.ContentMode.PROTECTED: MPMarshaller.ContentMode.VISIBLE,
|
(user._ext_mpw != null) && user._ext_mpw.hide_passwords,
|
||||||
|
export.redacted? MPMarshaller.ContentMode.PROTECTED: MPMarshaller.ContentMode.VISIBLE,
|
||||||
MPMarshalFormat.JSON, file
|
MPMarshalFormat.JSON, file
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -206,13 +198,21 @@ public class MPJSONFile 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
|
||||||
MPAlgorithm.Version algorithm;
|
MPAlgorithm.Version algorithm;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
Ext _ext_mpw;
|
||||||
|
|
||||||
|
|
||||||
|
public static class Ext extends MPJSONAnyObject {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
MPResultType default_type;
|
MPResultType default_type;
|
||||||
|
boolean hide_passwords;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user