2
0

Import & export users + improved user state tracking.

This commit is contained in:
Maarten Billemont
2018-07-29 14:01:07 -04:00
parent 18ecc41b39
commit 928b617ed0
26 changed files with 510 additions and 281 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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())