Import & export users + improved user state tracking.
This commit is contained in:
@@ -55,12 +55,11 @@ public interface MPUser<S extends MPSite<?>> extends Comparable<MPUser<?>> {
|
||||
/**
|
||||
* Performs an authentication attempt against the keyID for this user.
|
||||
*
|
||||
* Note: If a keyID is not set, authentication will always succeed and the keyID will be set to match the given master password.
|
||||
*
|
||||
* @param masterPassword The password to authenticate with.
|
||||
* You cannot re-use this array after passing it in, authentication will destroy its contents.
|
||||
*
|
||||
* @throws MPIncorrectMasterPasswordException If authentication fails due to the given master password not matching the user's keyID.
|
||||
* @apiNote If a keyID is not set, authentication will always succeed and the keyID will be set to match the given master password.
|
||||
* <b>This method destroys the contents of the {@code masterPassword} array.</b>
|
||||
*/
|
||||
void authenticate(char[] masterPassword)
|
||||
throws MPIncorrectMasterPasswordException, MPAlgorithmException;
|
||||
@@ -68,11 +67,10 @@ public interface MPUser<S extends MPSite<?>> extends Comparable<MPUser<?>> {
|
||||
/**
|
||||
* Performs an authentication attempt against the keyID for this user.
|
||||
*
|
||||
* Note: If a keyID is not set, authentication will always succeed and the keyID will be set to match the given key.
|
||||
*
|
||||
* @param masterKey The master key to authenticate with.
|
||||
*
|
||||
* @throws MPIncorrectMasterPasswordException If authentication fails due to the given master password not matching the user's keyID.
|
||||
* @apiNote If a keyID is not set, authentication will always succeed and the keyID will be set to match the given key.
|
||||
*/
|
||||
void authenticate(MPMasterKey masterKey)
|
||||
throws MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException;
|
||||
|
@@ -37,8 +37,9 @@ import javax.annotation.Nullable;
|
||||
*/
|
||||
public abstract class MPBasicUser<S extends MPBasicSite<?, ?>> extends Changeable implements MPUser<S> {
|
||||
|
||||
protected final Logger logger = Logger.get( getClass() );
|
||||
private final Set<Listener> listeners = new CopyOnWriteArraySet<>();
|
||||
private static final Logger logger = Logger.get( MPBasicUser.class );
|
||||
|
||||
private final Set<Listener> listeners = new CopyOnWriteArraySet<>();
|
||||
|
||||
private int avatar;
|
||||
private final String fullName;
|
||||
@@ -65,7 +66,7 @@ public abstract class MPBasicUser<S extends MPBasicSite<?, ?>> extends Changeabl
|
||||
|
||||
@Override
|
||||
public void setAvatar(final int avatar) {
|
||||
if (Objects.equals(this.avatar, avatar))
|
||||
if (Objects.equals( this.avatar, avatar ))
|
||||
return;
|
||||
|
||||
this.avatar = avatar;
|
||||
@@ -86,7 +87,7 @@ public abstract class MPBasicUser<S extends MPBasicSite<?, ?>> extends Changeabl
|
||||
|
||||
@Override
|
||||
public void setAlgorithm(final MPAlgorithm algorithm) {
|
||||
if (Objects.equals(this.algorithm, algorithm))
|
||||
if (Objects.equals( this.algorithm, algorithm ))
|
||||
return;
|
||||
|
||||
this.algorithm = algorithm;
|
||||
|
@@ -18,6 +18,7 @@
|
||||
|
||||
package com.lyndir.masterpassword.model.impl;
|
||||
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import com.lyndir.masterpassword.*;
|
||||
import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException;
|
||||
import com.lyndir.masterpassword.model.MPUser;
|
||||
@@ -36,6 +37,8 @@ import org.joda.time.ReadableInstant;
|
||||
@SuppressWarnings("ComparableImplementedButEqualsNotOverridden")
|
||||
public class MPFileUser extends MPBasicUser<MPFileSite> {
|
||||
|
||||
private static final Logger logger = Logger.get( MPFileUser.class );
|
||||
|
||||
@Nullable
|
||||
private byte[] keyID;
|
||||
private File path;
|
||||
@@ -46,13 +49,23 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
|
||||
private ReadableInstant lastUsed;
|
||||
private boolean complete;
|
||||
|
||||
public MPFileUser(final String fullName) {
|
||||
this( fullName, null, MPAlgorithm.Version.CURRENT.getAlgorithm() );
|
||||
@Nullable
|
||||
public static MPFileUser load(final File file)
|
||||
throws IOException, MPMarshalException {
|
||||
for (final MPMarshalFormat format : MPMarshalFormat.values())
|
||||
if (file.getName().endsWith( format.fileSuffix() ))
|
||||
return format.unmarshaller().readUser( file );
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPAlgorithm algorithm) {
|
||||
public MPFileUser(final String fullName, final File path) {
|
||||
this( fullName, null, MPAlgorithm.Version.CURRENT.getAlgorithm(), path );
|
||||
}
|
||||
|
||||
public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPAlgorithm algorithm, final File path) {
|
||||
this( fullName, keyID, algorithm, 0, null, new Instant(),
|
||||
MPMarshaller.ContentMode.PROTECTED, MPMarshalFormat.DEFAULT, MPFileUserManager.get().getPath() );
|
||||
MPMarshaller.ContentMode.PROTECTED, MPMarshalFormat.DEFAULT, path );
|
||||
}
|
||||
|
||||
public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPAlgorithm algorithm,
|
||||
@@ -74,6 +87,10 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
|
||||
return (keyID == null)? null: keyID.clone();
|
||||
}
|
||||
|
||||
public void setPath(final File path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlgorithm(final MPAlgorithm algorithm) {
|
||||
if (!algorithm.equals( getAlgorithm() ) && (keyID != null)) {
|
||||
@@ -99,7 +116,7 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
|
||||
}
|
||||
|
||||
public void setFormat(final MPMarshalFormat format) {
|
||||
if (Objects.equals(this.format, format))
|
||||
if (Objects.equals( this.format, format ))
|
||||
return;
|
||||
|
||||
this.format = format;
|
||||
@@ -111,7 +128,7 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
|
||||
}
|
||||
|
||||
public void setContentMode(final MPMarshaller.ContentMode contentMode) {
|
||||
if (Objects.equals(this.contentMode, contentMode))
|
||||
if (Objects.equals( this.contentMode, contentMode ))
|
||||
return;
|
||||
|
||||
this.contentMode = contentMode;
|
||||
@@ -123,7 +140,7 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
|
||||
}
|
||||
|
||||
public void setDefaultType(final MPResultType defaultType) {
|
||||
if (Objects.equals(this.defaultType, defaultType))
|
||||
if (Objects.equals( this.defaultType, defaultType ))
|
||||
return;
|
||||
|
||||
this.defaultType = defaultType;
|
||||
@@ -169,6 +186,19 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
|
||||
}
|
||||
}
|
||||
|
||||
public void save() {
|
||||
try {
|
||||
if (isComplete())
|
||||
getFormat().marshaller().marshall( this );
|
||||
}
|
||||
catch (final MPKeyUnavailableException e) {
|
||||
logger.wrn( e, "Cannot write out changes for unauthenticated user: %s.", this );
|
||||
}
|
||||
catch (final IOException | MPMarshalException | MPAlgorithmException e) {
|
||||
logger.err( e, "Unable to write out changes for user: %s", this );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
keyID = null;
|
||||
@@ -183,16 +213,7 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
|
||||
|
||||
@Override
|
||||
protected void onChanged() {
|
||||
try {
|
||||
if (isComplete())
|
||||
getFormat().marshaller().marshall( this );
|
||||
}
|
||||
catch (final MPKeyUnavailableException e) {
|
||||
logger.wrn( e, "Cannot write out changes for unauthenticated user: %s.", this );
|
||||
}
|
||||
catch (final IOException | MPMarshalException | MPAlgorithmException e) {
|
||||
logger.err( e, "Unable to write out changes for user: %s", this );
|
||||
}
|
||||
save();
|
||||
|
||||
super.onChanged();
|
||||
}
|
||||
|
@@ -78,31 +78,40 @@ public class MPFileUserManager {
|
||||
}
|
||||
|
||||
for (final File file : pathFiles)
|
||||
for (final MPMarshalFormat format : MPMarshalFormat.values())
|
||||
if (file.getName().endsWith( format.fileSuffix() ))
|
||||
try {
|
||||
MPFileUser user = format.unmarshaller().readUser( file );
|
||||
MPFileUser previousUser = userByName.put( user.getFullName(), user );
|
||||
if ((previousUser != null) && (previousUser.getFormat().ordinal() > user.getFormat().ordinal()))
|
||||
userByName.put( previousUser.getFullName(), previousUser );
|
||||
break;
|
||||
}
|
||||
catch (final IOException | MPMarshalException e) {
|
||||
logger.err( e, "Couldn't read user from: %s", file );
|
||||
}
|
||||
try {
|
||||
MPFileUser user = MPFileUser.load( file );
|
||||
if (user != null) {
|
||||
MPFileUser previousUser = userByName.put( user.getFullName(), user );
|
||||
if ((previousUser != null) && (previousUser.getFormat().ordinal() > user.getFormat().ordinal()))
|
||||
userByName.put( previousUser.getFullName(), previousUser );
|
||||
}
|
||||
}
|
||||
catch (final IOException | MPMarshalException e) {
|
||||
logger.err( e, "Couldn't read user from: %s", file );
|
||||
}
|
||||
|
||||
fireUpdated();
|
||||
}
|
||||
|
||||
public MPFileUser add(final String fullName) {
|
||||
MPFileUser user = new MPFileUser( fullName );
|
||||
userByName.put( user.getFullName(), user );
|
||||
return add( new MPFileUser( fullName, getPath() ) );
|
||||
}
|
||||
|
||||
public MPFileUser add(final MPFileUser user) {
|
||||
user.setPath( getPath() );
|
||||
user.save();
|
||||
|
||||
MPFileUser oldUser = userByName.put( user.getFullName(), user );
|
||||
if (oldUser != null)
|
||||
oldUser.invalidate();
|
||||
fireUpdated();
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
public void delete(final MPFileUser user) {
|
||||
user.invalidate();
|
||||
|
||||
// Remove deleted users.
|
||||
File userFile = user.getFile();
|
||||
if (userFile.exists() && !userFile.delete())
|
||||
|
Reference in New Issue
Block a user