diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyAlgorithm.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithm.java
similarity index 94%
rename from core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyAlgorithm.java
rename to core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithm.java
index 9bbc4763..0442139e 100644
--- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyAlgorithm.java
+++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithm.java
@@ -24,11 +24,11 @@ import javax.annotation.Nullable;
/**
- * @see MasterKey.Version
+ * @see MPMasterKey.Version
*/
-public interface MasterKeyAlgorithm extends Serializable {
+public interface MPAlgorithm extends Serializable {
- MasterKey.Version getAlgorithmVersion();
+ MPMasterKey.Version getAlgorithmVersion();
byte[] deriveKey(String fullName, char[] masterPassword);
diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV0.java
similarity index 98%
rename from core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java
rename to core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV0.java
index 505d73a8..c50456e7 100644
--- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java
+++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV0.java
@@ -38,11 +38,11 @@ import javax.crypto.IllegalBlockSizeException;
/**
- * @see MasterKey.Version#V0
+ * @see MPMasterKey.Version#V0
*
* @author lhunath, 2014-08-30
*/
-public class MasterKeyV0 implements MasterKeyAlgorithm {
+public class MPAlgorithmV0 implements MPAlgorithm {
/**
* mpw: validity for the time-based rolling counter.
@@ -84,9 +84,9 @@ public class MasterKeyV0 implements MasterKeyAlgorithm {
protected final Logger logger = Logger.get( getClass() );
@Override
- public MasterKey.Version getAlgorithmVersion() {
+ public MPMasterKey.Version getAlgorithmVersion() {
- return MasterKey.Version.V0;
+ return MPMasterKey.Version.V0;
}
@Override
diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV1.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV1.java
similarity index 93%
rename from core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV1.java
rename to core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV1.java
index 57b2d508..e1ecc4de 100644
--- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV1.java
+++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV1.java
@@ -23,16 +23,16 @@ import javax.annotation.Nullable;
/**
- * @see MasterKey.Version#V1
+ * @see MPMasterKey.Version#V1
*
* @author lhunath, 2014-08-30
*/
-public class MasterKeyV1 extends MasterKeyV0 {
+public class MPAlgorithmV1 extends MPAlgorithmV0 {
@Override
- public MasterKey.Version getAlgorithmVersion() {
+ public MPMasterKey.Version getAlgorithmVersion() {
- return MasterKey.Version.V1;
+ return MPMasterKey.Version.V1;
}
@Override
diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV2.java
similarity index 84%
rename from core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java
rename to core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV2.java
index 34df547d..ef76c083 100644
--- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java
+++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV2.java
@@ -28,16 +28,16 @@ import javax.annotation.Nullable;
/**
- * @see MasterKey.Version#V2
+ * @see MPMasterKey.Version#V2
*
* @author lhunath, 2014-08-30
*/
-public class MasterKeyV2 extends MasterKeyV1 {
+public class MPAlgorithmV2 extends MPAlgorithmV1 {
@Override
- public MasterKey.Version getAlgorithmVersion() {
+ public MPMasterKey.Version getAlgorithmVersion() {
- return MasterKey.Version.V2;
+ return MPMasterKey.Version.V2;
}
@Override
@@ -56,25 +56,25 @@ public class MasterKeyV2 extends MasterKeyV1 {
// OTP counter value.
if (siteCounter.longValue() == 0)
- siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (MasterKeyV0.mpw_otp_window * 1000)) * MasterKeyV0.mpw_otp_window );
+ siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (MPAlgorithmV0.mpw_otp_window * 1000)) * MPAlgorithmV0.mpw_otp_window );
// Calculate the site seed.
- byte[] siteNameBytes = siteName.getBytes( MasterKeyV0.mpw_charset );
+ byte[] siteNameBytes = siteName.getBytes( MPAlgorithmV0.mpw_charset );
byte[] siteNameLengthBytes = bytesForInt( siteNameBytes.length );
byte[] siteCounterBytes = bytesForInt( siteCounter );
- byte[] keyContextBytes = ((keyContext == null) || keyContext.isEmpty())? null: keyContext.getBytes( MasterKeyV0.mpw_charset );
+ byte[] keyContextBytes = ((keyContext == null) || keyContext.isEmpty())? null: keyContext.getBytes( MPAlgorithmV0.mpw_charset );
byte[] keyContextLengthBytes = (keyContextBytes == null)? null: bytesForInt( keyContextBytes.length );
logger.trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s",
keyScope, CodeUtils.encodeHex( siteNameLengthBytes ), siteName, CodeUtils.encodeHex( siteCounterBytes ),
(keyContextLengthBytes == null)? null: CodeUtils.encodeHex( keyContextLengthBytes ), keyContext );
- byte[] sitePasswordInfo = Bytes.concat( keyScope.getBytes( MasterKeyV0.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
+ byte[] sitePasswordInfo = Bytes.concat( keyScope.getBytes( MPAlgorithmV0.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
if (keyContextBytes != null)
sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes );
logger.trc( " => siteSalt.id: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", (Object) idForBytes( masterKey ) );
- byte[] sitePasswordSeedBytes = MasterKeyV0.mpw_digest.of( masterKey, sitePasswordInfo );
+ byte[] sitePasswordSeedBytes = MPAlgorithmV0.mpw_digest.of( masterKey, sitePasswordInfo );
logger.trc( " => siteKey.id: %s", (Object) idForBytes( sitePasswordSeedBytes ) );
return sitePasswordSeedBytes;
diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV3.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV3.java
similarity index 84%
rename from core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV3.java
rename to core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV3.java
index fff39699..497c858d 100644
--- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV3.java
+++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV3.java
@@ -29,25 +29,25 @@ import java.util.Arrays;
/**
- * @see MasterKey.Version#V3
+ * @see MPMasterKey.Version#V3
*
* @author lhunath, 2014-08-30
*/
-public class MasterKeyV3 extends MasterKeyV2 {
+public class MPAlgorithmV3 extends MPAlgorithmV2 {
@Override
- public MasterKey.Version getAlgorithmVersion() {
+ public MPMasterKey.Version getAlgorithmVersion() {
- return MasterKey.Version.V3;
+ return MPMasterKey.Version.V3;
}
@Override
public byte[] deriveKey(final String fullName, final char[] masterPassword) {
Preconditions.checkArgument( masterPassword.length > 0 );
- byte[] fullNameBytes = fullName.getBytes( MasterKeyV0.mpw_charset );
+ byte[] fullNameBytes = fullName.getBytes( MPAlgorithmV0.mpw_charset );
byte[] fullNameLengthBytes = MPUtils.bytesForInt( fullNameBytes.length );
- ByteBuffer mpBytesBuf = MasterKeyV0.mpw_charset.encode( CharBuffer.wrap( masterPassword ) );
+ ByteBuffer mpBytesBuf = MPAlgorithmV0.mpw_charset.encode( CharBuffer.wrap( masterPassword ) );
logger.trc( "-- mpw_masterKey (algorithm: %u)", getAlgorithmVersion().toInt() );
logger.trc( "fullName: %s", fullName );
@@ -59,12 +59,12 @@ public class MasterKeyV3 extends MasterKeyV2 {
// Calculate the master key salt.
logger.trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s",
keyScope, CodeUtils.encodeHex( fullNameLengthBytes ), fullName );
- byte[] masterKeySalt = Bytes.concat( keyScope.getBytes( MasterKeyV0.mpw_charset ), fullNameLengthBytes, fullNameBytes );
+ byte[] masterKeySalt = Bytes.concat( keyScope.getBytes( MPAlgorithmV0.mpw_charset ), fullNameLengthBytes, fullNameBytes );
logger.trc( " => masterKeySalt.id: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
// Calculate the master key.
logger.trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%lu, r=%u, p=%u )",
- MasterKeyV0.scrypt_N, MasterKeyV0.scrypt_r, MasterKeyV0.scrypt_p );
+ MPAlgorithmV0.scrypt_N, MPAlgorithmV0.scrypt_r, MPAlgorithmV0.scrypt_p );
byte[] mpBytes = new byte[mpBytesBuf.remaining()];
mpBytesBuf.get( mpBytes, 0, mpBytes.length );
Arrays.fill( mpBytesBuf.array(), (byte) 0 );
diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPInvalidatedException.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPInvalidatedException.java
new file mode 100644
index 00000000..53802f37
--- /dev/null
+++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPInvalidatedException.java
@@ -0,0 +1,25 @@
+//==============================================================================
+// This file is part of Master Password.
+// Copyright (c) 2011-2017, Maarten Billemont.
+//
+// Master Password is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Master Password is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You can find a copy of the GNU General Public License in the
+// LICENSE file. Alternatively, see .
+//==============================================================================
+
+package com.lyndir.masterpassword;
+
+/**
+ * @author lhunath, 2017-09-21
+ */
+public class MPInvalidatedException extends Exception {
+}
diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPMasterKey.java
similarity index 63%
rename from core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java
rename to core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPMasterKey.java
index 94f048cc..e2f23589 100644
--- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java
+++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPMasterKey.java
@@ -20,9 +20,10 @@ package com.lyndir.masterpassword;
import static com.lyndir.masterpassword.MPUtils.idForBytes;
-import com.google.common.base.Preconditions;
import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.logging.Logger;
+import java.util.Arrays;
+import java.util.EnumMap;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -30,26 +31,28 @@ import javax.annotation.Nullable;
/**
* @author lhunath, 2014-08-30
*/
-public class MasterKey {
+public class MPMasterKey {
@SuppressWarnings("UnusedDeclaration")
- private static final Logger logger = Logger.get( MasterKey.class );
+ private static final Logger logger = Logger.get( MPMasterKey.class );
+ private final EnumMap keyByVersion = new EnumMap<>( Version.class );
private final String fullName;
private final char[] masterPassword;
+ private boolean invalidated;
+
+ /**
+ * @param masterPassword The characters of the user's master password. Note: this array is held by reference and its contents
+ * invalidated on {@link #invalidate()}.
+ */
@SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter")
- public MasterKey(final String fullName, final char[] masterPassword) {
+ public MPMasterKey(final String fullName, final char[] masterPassword) {
this.fullName = fullName;
this.masterPassword = masterPassword;
}
- private byte[] getKey(final Version algorithmVersion) {
- // TODO: Cache keys.
- return algorithmVersion.getAlgorithm().deriveKey( fullName, masterPassword );
- }
-
/**
* Generate a site result token.
*
@@ -60,10 +63,13 @@ public class MasterKey {
* @param resultType The type of result to generate.
* @param resultParam A parameter for the resultType. For stateful result types, the output of
* {@link #siteState(String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String, Version)}.
+ *
+ * @throws MPInvalidatedException {@link #invalidate()} has been called on this object.
*/
public String siteResult(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
@Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam,
- final Version algorithmVersion) {
+ final Version algorithmVersion)
+ throws MPInvalidatedException {
return algorithmVersion.getAlgorithm().siteResult(
getKey( algorithmVersion ), siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam );
}
@@ -78,10 +84,13 @@ public class MasterKey {
* @param resultType The type of result token to encrypt.
* @param resultParam The result token desired from
* {@link #siteResult(String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String, Version)}.
+ *
+ * @throws MPInvalidatedException {@link #invalidate()} has been called on this object.
*/
public String siteState(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
@Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam,
- final Version algorithmVersion) {
+ final Version algorithmVersion)
+ throws MPInvalidatedException {
return algorithmVersion.getAlgorithm().siteState(
getKey( algorithmVersion ), siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam );
}
@@ -92,45 +101,81 @@ public class MasterKey {
return fullName;
}
- public byte[] getKeyID(final Version algorithmVersion) {
+ /**
+ * Calculate an identifier for the master key.
+ *
+ * @throws MPInvalidatedException {@link #invalidate()} has been called on this object.
+ */
+ public byte[] getKeyID(final Version algorithmVersion)
+ throws MPInvalidatedException {
return idForBytes( getKey( algorithmVersion ) );
}
+ /**
+ * Wipe this key's secrets from memory, making the object permanently unusable.
+ */
+ public void invalidate() {
+
+ invalidated = true;
+ for (final byte[] key : keyByVersion.values())
+ Arrays.fill( key, (byte) 0 );
+ Arrays.fill( masterPassword, (char) 0 );
+ }
+
+ private byte[] getKey(final Version algorithmVersion)
+ throws MPInvalidatedException {
+ if (invalidated)
+ throw new MPInvalidatedException();
+
+ byte[] key = keyByVersion.get( algorithmVersion );
+ if (key == null)
+ keyByVersion.put( algorithmVersion, key = algorithmVersion.getAlgorithm().deriveKey( fullName, masterPassword ) );
+
+ return key;
+ }
+
+ /**
+ * The algorithm iterations.
+ */
public enum Version {
+
/**
* bugs:
* - does math with chars whose signedness was platform-dependent.
* - miscounted the byte-length for multi-byte site names.
- * - miscounted the byte-length for multi-byte full names.
+ * - miscounted the byte-length for multi-byte user names.
*/
- V0( new MasterKeyV0() ),
+ V0( new MPAlgorithmV0() ),
+
/**
* bugs:
* - miscounted the byte-length for multi-byte site names.
- * - miscounted the byte-length for multi-byte full names.
+ * - miscounted the byte-length for multi-byte user names.
*/
- V1( new MasterKeyV1() ),
+ V1( new MPAlgorithmV1() ),
+
/**
* bugs:
- * - miscounted the byte-length for multi-byte full names.
+ * - miscounted the byte-length for multi-byte user names.
*/
- V2( new MasterKeyV2() ),
+ V2( new MPAlgorithmV2() ),
+
/**
* bugs:
* - no known issues.
*/
- V3( new MasterKeyV3() );
+ V3( new MPAlgorithmV3() );
public static final Version CURRENT = V3;
- private final MasterKeyAlgorithm algorithm;
+ private final MPAlgorithm algorithm;
- Version(final MasterKeyAlgorithm algorithm) {
+ Version(final MPAlgorithm algorithm) {
this.algorithm = algorithm;
}
- public MasterKeyAlgorithm getAlgorithm() {
+ public MPAlgorithm getAlgorithm() {
return algorithm;
}
diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPUtils.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPUtils.java
index b81b6486..6b50f0da 100644
--- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPUtils.java
+++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPUtils.java
@@ -28,14 +28,14 @@ import java.nio.ByteBuffer;
public final class MPUtils {
public static byte[] bytesForInt(final int number) {
- return ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( MasterKeyV0.mpw_byteOrder ).putInt( number ).array();
+ return ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( MPAlgorithmV0.mpw_byteOrder ).putInt( number ).array();
}
public static byte[] bytesForInt(final UnsignedInteger number) {
- return ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( MasterKeyV0.mpw_byteOrder ).putInt( number.intValue() ).array();
+ return ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( MPAlgorithmV0.mpw_byteOrder ).putInt( number.intValue() ).array();
}
public static byte[] idForBytes(final byte[] bytes) {
- return MasterKeyV0.mpw_hash.of( bytes );
+ return MPAlgorithmV0.mpw_hash.of( bytes );
}
}
diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileSite.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileSite.java
new file mode 100644
index 00000000..a08f9554
--- /dev/null
+++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileSite.java
@@ -0,0 +1,205 @@
+//==============================================================================
+// This file is part of Master Password.
+// Copyright (c) 2011-2017, Maarten Billemont.
+//
+// Master Password is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Master Password is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You can find a copy of the GNU General Public License in the
+// LICENSE file. Alternatively, see .
+//==============================================================================
+
+package com.lyndir.masterpassword.model;
+
+import com.google.common.primitives.UnsignedInteger;
+import com.lyndir.masterpassword.*;
+import javax.annotation.Nullable;
+import org.joda.time.Instant;
+
+
+/**
+ * @author lhunath, 14-12-05
+ */
+public class MPFileSite extends MPSite {
+
+ private final MPFileUser user;
+ private String siteName;
+ @Nullable
+ private String siteContent;
+ private UnsignedInteger siteCounter;
+ private MPResultType resultType;
+ private MPMasterKey.Version algorithmVersion;
+
+ @Nullable
+ private String loginContent;
+ @Nullable
+ private MPResultType loginType;
+
+ @Nullable
+ private String url;
+ private int uses;
+ private Instant lastUsed;
+
+ public MPFileSite(final MPFileUser user, final String siteName) {
+ this( user, siteName, DEFAULT_COUNTER, MPResultType.DEFAULT, MPMasterKey.Version.CURRENT );
+ }
+
+ public MPFileSite(final MPFileUser user, final String siteName, final UnsignedInteger siteCounter, final MPResultType resultType,
+ final MPMasterKey.Version algorithmVersion) {
+ this.user = user;
+ this.siteName = siteName;
+ this.siteCounter = siteCounter;
+ this.resultType = resultType;
+ this.algorithmVersion = algorithmVersion;
+ this.lastUsed = new Instant();
+ }
+
+ protected MPFileSite(final MPFileUser user, final String siteName, @Nullable final String siteContent,
+ final UnsignedInteger siteCounter,
+ final MPResultType resultType, final MPMasterKey.Version algorithmVersion,
+ @Nullable final String loginContent, @Nullable final MPResultType loginType,
+ @Nullable final String url, final int uses, final Instant lastUsed) {
+ this.user = user;
+ this.siteName = siteName;
+ this.siteContent = siteContent;
+ this.siteCounter = siteCounter;
+ this.resultType = resultType;
+ this.algorithmVersion = algorithmVersion;
+ this.loginContent = loginContent;
+ this.loginType = loginType;
+ this.url = url;
+ this.uses = uses;
+ this.lastUsed = lastUsed;
+ }
+
+ public String resultFor(final MPMasterKey masterKey)
+ throws MPInvalidatedException {
+
+ return resultFor( masterKey, MPKeyPurpose.Authentication, null );
+ }
+
+ public String resultFor(final MPMasterKey masterKey, final MPKeyPurpose keyPurpose, @Nullable final String keyContext)
+ throws MPInvalidatedException {
+
+ return resultFor( masterKey, keyPurpose, keyContext, getSiteContent() );
+ }
+
+ public String loginFor(final MPMasterKey masterKey)
+ throws MPInvalidatedException {
+
+ if (loginType == null)
+ loginType = MPResultType.GeneratedName;
+
+ return loginFor( masterKey, loginType, loginContent );
+ }
+
+ public MPFileUser getUser() {
+ return user;
+ }
+
+ @Override
+ public String getSiteName() {
+ return siteName;
+ }
+
+ @Override
+ public void setSiteName(final String siteName) {
+ this.siteName = siteName;
+ }
+
+ @Nullable
+ public String getSiteContent() {
+ return siteContent;
+ }
+
+ public void setSitePassword(final MPMasterKey masterKey, @Nullable final MPResultType resultType, @Nullable final String result)
+ throws MPInvalidatedException {
+ this.resultType = resultType;
+ if (result == null)
+ this.siteContent = null;
+ else
+ this.siteContent = masterKey.siteState(
+ getSiteName(), getSiteCounter(), MPKeyPurpose.Authentication, null, getResultType(), result, getAlgorithmVersion() );
+ }
+
+ @Override
+ public UnsignedInteger getSiteCounter() {
+ return siteCounter;
+ }
+
+ @Override
+ public void setSiteCounter(final UnsignedInteger siteCounter) {
+ this.siteCounter = siteCounter;
+ }
+
+ @Override
+ public MPResultType getResultType() {
+ return resultType;
+ }
+
+ @Override
+ public void setResultType(final MPResultType resultType) {
+ this.resultType = resultType;
+ }
+
+ @Override
+ public MPMasterKey.Version getAlgorithmVersion() {
+ return algorithmVersion;
+ }
+
+ @Override
+ public void setAlgorithmVersion(final MPMasterKey.Version algorithmVersion) {
+ this.algorithmVersion = algorithmVersion;
+ }
+
+ @Nullable
+ public MPResultType getLoginType() {
+ return loginType;
+ }
+
+ @Nullable
+ public String getLoginContent() {
+ return loginContent;
+ }
+
+ public void setLoginName(final MPMasterKey masterKey, @Nullable final MPResultType loginType, @Nullable final String result)
+ throws MPInvalidatedException {
+ this.loginType = loginType;
+ if (this.loginType != null)
+ if (result == null)
+ this.loginContent = null;
+ else
+ this.loginContent = masterKey.siteState(
+ siteName, DEFAULT_COUNTER, MPKeyPurpose.Identification, null, this.loginType, result, algorithmVersion );
+ }
+
+ @Nullable
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(@Nullable final String url) {
+ this.url = url;
+ }
+
+ public int getUses() {
+ return uses;
+ }
+
+ public Instant getLastUsed() {
+ return lastUsed;
+ }
+
+ public void use() {
+ uses++;
+ lastUsed = new Instant();
+ user.use();
+ }
+}
diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileUser.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileUser.java
new file mode 100755
index 00000000..e2f428e6
--- /dev/null
+++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileUser.java
@@ -0,0 +1,172 @@
+//==============================================================================
+// This file is part of Master Password.
+// Copyright (c) 2011-2017, Maarten Billemont.
+//
+// Master Password is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Master Password is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You can find a copy of the GNU General Public License in the
+// LICENSE file. Alternatively, see .
+//==============================================================================
+
+package com.lyndir.masterpassword.model;
+
+import com.google.common.collect.*;
+import com.lyndir.lhunath.opal.system.logging.Logger;
+import com.lyndir.masterpassword.*;
+import java.util.*;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.joda.time.*;
+
+
+/**
+ * @author lhunath, 14-12-07
+ */
+public class MPFileUser extends MPUser implements Comparable {
+
+ @SuppressWarnings("UnusedDeclaration")
+ private static final Logger logger = Logger.get( MPFileUser.class );
+
+ private final String fullName;
+ private final Collection sites = Sets.newHashSet();
+
+ @Nullable
+ private byte[] keyID;
+ private MPMasterKey.Version algorithmVersion;
+
+ private int avatar;
+ private MPResultType defaultType;
+ private ReadableInstant lastUsed;
+
+ public MPFileUser(final String fullName) {
+ this( fullName, null, MPMasterKey.Version.CURRENT );
+ }
+
+ public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPMasterKey.Version algorithmVersion) {
+ this( fullName, keyID, algorithmVersion, 0, MPResultType.DEFAULT, new Instant() );
+ }
+
+ public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPMasterKey.Version algorithmVersion, final int avatar,
+ final MPResultType defaultType, final ReadableInstant lastUsed) {
+ this.fullName = fullName;
+ this.keyID = (keyID == null)? null: keyID.clone();
+ this.algorithmVersion = algorithmVersion;
+ this.avatar = avatar;
+ this.defaultType = defaultType;
+ this.lastUsed = lastUsed;
+ }
+
+ @Override
+ public String getFullName() {
+ return fullName;
+ }
+
+ @Override
+ public MPMasterKey.Version getAlgorithmVersion() {
+ return algorithmVersion;
+ }
+
+ public void setAlgorithmVersion(final MPMasterKey.Version algorithmVersion) {
+ this.algorithmVersion = algorithmVersion;
+ }
+
+ @Override
+ public int getAvatar() {
+ return avatar;
+ }
+
+ public void setAvatar(final int avatar) {
+ this.avatar = avatar;
+ }
+
+ public MPResultType getDefaultType() {
+ return defaultType;
+ }
+
+ public void setDefaultType(final MPResultType defaultType) {
+ this.defaultType = defaultType;
+ }
+
+ public ReadableInstant getLastUsed() {
+ return lastUsed;
+ }
+
+ public void use() {
+ lastUsed = new Instant();
+ }
+
+ public Iterable getSites() {
+ return sites;
+ }
+
+ @Override
+ public void addSite(final MPFileSite site) {
+ sites.add( site );
+ }
+
+ @Override
+ public void deleteSite(final MPFileSite site) {
+ sites.remove( site );
+ }
+
+ @Override
+ public Collection findSites(final String query) {
+ ImmutableList.Builder results = ImmutableList.builder();
+ for (final MPFileSite site : getSites())
+ if (site.getSiteName().startsWith( query ))
+ results.add( site );
+
+ return results.build();
+ }
+
+ /**
+ * Performs an authentication attempt against the keyID for this user.
+ *
+ * Note: If this user doesn't have a keyID set yet, authentication will always succeed and the key ID will be set as a result.
+ *
+ * @param masterPassword The password to authenticate with.
+ *
+ * @return The master key for the user if authentication was successful.
+ *
+ * @throws MPIncorrectMasterPasswordException If authentication fails due to the given master password not matching the user's keyID.
+ */
+ @Nonnull
+ @Override
+ public MPMasterKey authenticate(final char[] masterPassword)
+ throws MPIncorrectMasterPasswordException {
+ try {
+ key = new MPMasterKey( getFullName(), masterPassword );
+ if ((keyID == null) || (keyID.length == 0))
+ keyID = key.getKeyID( algorithmVersion );
+ else if (!Arrays.equals( key.getKeyID( algorithmVersion ), keyID ))
+ throw new MPIncorrectMasterPasswordException( this );
+
+ return key;
+ }
+ catch (final MPInvalidatedException e) {
+ throw logger.bug( e );
+ }
+ }
+
+ void save()
+ throws MPInvalidatedException {
+ MPFileUserManager.get().save( this, getMasterKey() );
+ }
+
+ @Override
+ public int compareTo(final MPFileUser o) {
+ int comparison = getLastUsed().compareTo( o.getLastUsed() );
+ if (comparison == 0)
+ comparison = getFullName().compareTo( o.getFullName() );
+
+ return comparison;
+ }
+}
diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUserFileManager.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileUserManager.java
similarity index 64%
rename from core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUserFileManager.java
rename to core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileUserManager.java
index 0ebbe1af..2400264d 100644
--- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUserFileManager.java
+++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileUserManager.java
@@ -24,20 +24,22 @@ import com.google.common.base.*;
import com.google.common.collect.*;
import com.google.common.io.CharSink;
import com.lyndir.lhunath.opal.system.logging.Logger;
-import com.lyndir.masterpassword.MPConstant;
+import com.lyndir.masterpassword.*;
import java.io.*;
+import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Manages user data stored in user-specific {@code .mpsites} files under {@code .mpw.d}.
+ *
* @author lhunath, 14-12-07
*/
-public class MPUserFileManager extends MPUserManager {
+public class MPFileUserManager extends MPUserManager {
@SuppressWarnings("UnusedDeclaration")
- private static final Logger logger = Logger.get( MPUserFileManager.class );
- private static final MPUserFileManager instance;
+ private static final Logger logger = Logger.get( MPFileUserManager.class );
+ private static final MPFileUserManager instance;
static {
String rcDir = System.getenv( MPConstant.env_rcDir );
@@ -49,31 +51,31 @@ public class MPUserFileManager extends MPUserManager {
private final File userFilesDirectory;
- public static MPUserFileManager get() {
+ public static MPFileUserManager get() {
MPUserManager.instance = instance;
return instance;
}
- public static MPUserFileManager create(final File userFilesDirectory) {
- return new MPUserFileManager( userFilesDirectory );
+ public static MPFileUserManager create(final File userFilesDirectory) {
+ return new MPFileUserManager( userFilesDirectory );
}
- protected MPUserFileManager(final File userFilesDirectory) {
+ protected MPFileUserManager(final File userFilesDirectory) {
super( unmarshallUsers( userFilesDirectory ) );
this.userFilesDirectory = userFilesDirectory;
}
- private static Iterable unmarshallUsers(final File userFilesDirectory) {
+ private static Iterable unmarshallUsers(final File userFilesDirectory) {
if (!userFilesDirectory.mkdirs() && !userFilesDirectory.isDirectory()) {
logger.err( "Couldn't create directory for user files: %s", userFilesDirectory );
return ImmutableList.of();
}
- return FluentIterable.from( listUserFiles( userFilesDirectory ) ).transform( new Function() {
+ return FluentIterable.from( listUserFiles( userFilesDirectory ) ).transform( new Function() {
@Nullable
@Override
- public MPUser apply(@Nullable final File file) {
+ public MPFileUser apply(@Nullable final File file) {
try {
return new MPFlatUnmarshaller().unmarshall( Preconditions.checkNotNull( file ) );
}
@@ -95,42 +97,37 @@ public class MPUserFileManager extends MPUserManager {
}
@Override
- public void addUser(final MPUser user) {
- super.addUser( user );
- save();
- }
-
- @Override
- public void deleteUser(final MPUser user) {
+ public void deleteUser(final MPFileUser user) {
super.deleteUser( user );
- save();
+
+ // Remove deleted users.
+ File userFile = getUserFile( user );
+ if (userFile.exists() && !userFile.delete())
+ logger.err( "Couldn't delete file: %s", userFile );
}
/**
* Write the current user state to disk.
*/
- public void save() {
- // Save existing users.
- for (final MPUser user : getUsers())
- try {
- new CharSink() {
- @Override
- public Writer openStream()
- throws IOException {
- File mpsitesFile = new File( userFilesDirectory, user.getFullName() + ".mpsites" );
- return new OutputStreamWriter( new FileOutputStream( mpsitesFile ), Charsets.UTF_8 );
- }
- }.write( new MPFlatMarshaller().marshall( user, null/*TODO: masterKey*/, MPMarshaller.ContentMode.PROTECTED ) );
- }
- catch (final IOException e) {
- logger.err( e, "Unable to save sites for user: %s", user );
- }
+ public void save(final MPFileUser user, final MPMasterKey masterKey)
+ throws MPInvalidatedException {
+ try {
+ new CharSink() {
+ @Override
+ public Writer openStream()
+ throws IOException {
+ return new OutputStreamWriter( new FileOutputStream( getUserFile( user ) ), Charsets.UTF_8 );
+ }
+ }.write( new MPFlatMarshaller().marshall( user, masterKey, MPMarshaller.ContentMode.PROTECTED ) );
+ }
+ catch (final IOException e) {
+ logger.err( e, "Unable to save sites for user: %s", user );
+ }
+ }
- // Remove deleted users.
- for (final File userFile : listUserFiles( userFilesDirectory ))
- if (getUserNamed( userFile.getName().replaceFirst( "\\.mpsites$", "" ) ) == null)
- if (!userFile.delete())
- logger.err( "Couldn't delete file: %s", userFile );
+ @Nonnull
+ private File getUserFile(final MPFileUser user) {
+ return new File( userFilesDirectory, user.getFullName() + ".mpsites" );
}
/**
diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFlatMarshaller.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFlatMarshaller.java
index 6344a8ef..ac41a400 100644
--- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFlatMarshaller.java
+++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFlatMarshaller.java
@@ -21,8 +21,7 @@ package com.lyndir.masterpassword.model;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse;
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
-import com.lyndir.masterpassword.MPConstant;
-import com.lyndir.masterpassword.MasterKey;
+import com.lyndir.masterpassword.*;
import org.joda.time.Instant;
@@ -34,7 +33,8 @@ public class MPFlatMarshaller implements MPMarshaller {
private static final int FORMAT = 1;
@Override
- public String marshall(final MPUser user, final MasterKey masterKey, final ContentMode contentMode) {
+ public String marshall(final MPFileUser user, final MPMasterKey masterKey, final ContentMode contentMode)
+ throws MPInvalidatedException {
StringBuilder content = new StringBuilder();
content.append( "# Master Password site export\n" );
content.append( "# " ).append( contentMode.description() ).append( '\n' );
@@ -46,7 +46,7 @@ public class MPFlatMarshaller implements MPMarshaller {
content.append( "# Full Name: " ).append( user.getFullName() ).append( '\n' );
content.append( "# Avatar: " ).append( user.getAvatar() ).append( '\n' );
content.append( "# Key ID: " ).append( user.exportKeyID() ).append( '\n' );
- content.append( "# Algorithm: " ).append( MasterKey.Version.CURRENT.toInt() ).append( '\n' );
+ content.append( "# Algorithm: " ).append( MPMasterKey.Version.CURRENT.toInt() ).append( '\n' );
content.append( "# Default Type: " ).append( user.getDefaultType().getType() ).append( '\n' );
content.append( "# Passwords: " ).append( contentMode.name() ).append( '\n' );
content.append( "##\n" );
@@ -54,7 +54,7 @@ public class MPFlatMarshaller implements MPMarshaller {
content.append( "# Last Times Password Login\t Site\tSite\n" );
content.append( "# used used type name\t name\tpassword\n" );
- for (final MPSite site : user.getSites()) {
+ for (final MPFileSite site : user.getSites()) {
String loginName = site.getLoginContent();
String password = site.getSiteContent();
if (!contentMode.isRedacted()) {
diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFlatUnmarshaller.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFlatUnmarshaller.java
index bc97ab14..639d9b70 100644
--- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFlatUnmarshaller.java
+++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFlatUnmarshaller.java
@@ -44,7 +44,7 @@ public class MPFlatUnmarshaller implements MPUnmarshaller {
@Nonnull
@Override
- public MPUser unmarshall(@Nonnull final File file)
+ public MPFileUser unmarshall(@Nonnull final File file)
throws IOException {
try (Reader reader = new InputStreamReader( new FileInputStream( file ), Charsets.UTF_8 )) {
return unmarshall( CharStreams.toString( reader ) );
@@ -53,8 +53,8 @@ public class MPFlatUnmarshaller implements MPUnmarshaller {
@Nonnull
@Override
- public MPUser unmarshall(@Nonnull final String content) {
- MPUser user = null;
+ public MPFileUser unmarshall(@Nonnull final String content) {
+ MPFileUser user = null;
byte[] keyID = null;
String fullName = null;
int mpVersion = 0, importFormat = 0, avatar = 0;
@@ -70,7 +70,7 @@ public class MPFlatUnmarshaller implements MPUnmarshaller {
headerStarted = true;
else
// Ends the header.
- user = new MPUser( fullName, keyID, MasterKey.Version.fromInt( mpVersion ), avatar, defaultType, new DateTime( 0 ) );
+ user = new MPFileUser( fullName, keyID, MPMasterKey.Version.fromInt( mpVersion ), avatar, defaultType, new DateTime( 0 ) );
// Comment.
else if (line.startsWith( "#" )) {
@@ -103,28 +103,28 @@ public class MPFlatUnmarshaller implements MPUnmarshaller {
if (!siteMatcher.matches())
return null;
- MPSite site;
+ MPFileSite site;
switch (importFormat) {
case 0:
- site = new MPSite( user, //
- siteMatcher.group( 5 ), siteMatcher.group( 6 ), MPSite.DEFAULT_COUNTER,
- MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
- MasterKey.Version.fromInt( ConversionUtils.toIntegerNN(
+ site = new MPFileSite( user, //
+ siteMatcher.group( 5 ), siteMatcher.group( 6 ), MPFileSite.DEFAULT_COUNTER,
+ MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
+ MPMasterKey.Version.fromInt( ConversionUtils.toIntegerNN(
colon.matcher( siteMatcher.group( 4 ) ).replaceAll( "" ) ) ),
- null, null, null, ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ),
- MPConstant.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() );
+ null, null, null, ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ),
+ MPConstant.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() );
break;
case 1:
- site = new MPSite( user, //
- siteMatcher.group( 7 ), siteMatcher.group( 8 ),
- UnsignedInteger.valueOf( colon.matcher( siteMatcher.group( 5 ) ).replaceAll( "" ) ),
- MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
- MasterKey.Version.fromInt( ConversionUtils.toIntegerNN(
+ site = new MPFileSite( user, //
+ siteMatcher.group( 7 ), siteMatcher.group( 8 ),
+ UnsignedInteger.valueOf( colon.matcher( siteMatcher.group( 5 ) ).replaceAll( "" ) ),
+ MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
+ MPMasterKey.Version.fromInt( ConversionUtils.toIntegerNN(
colon.matcher( siteMatcher.group( 4 ) ).replaceAll( "" ) ) ),
- siteMatcher.group( 6 ), MPResultType.GeneratedName, null,
- ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ),
- MPConstant.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() );
+ siteMatcher.group( 6 ), MPResultType.GeneratedName, null,
+ ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ),
+ MPConstant.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() );
break;
default:
diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/IncorrectMasterPasswordException.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPIncorrectMasterPasswordException.java
similarity index 84%
rename from core/java/model/src/main/java/com/lyndir/masterpassword/model/IncorrectMasterPasswordException.java
rename to core/java/model/src/main/java/com/lyndir/masterpassword/model/MPIncorrectMasterPasswordException.java
index d62d6910..7d899b4f 100644
--- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/IncorrectMasterPasswordException.java
+++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPIncorrectMasterPasswordException.java
@@ -21,17 +21,17 @@ package com.lyndir.masterpassword.model;
/**
* @author lhunath, 14-12-17
*/
-public class IncorrectMasterPasswordException extends Exception {
+public class MPIncorrectMasterPasswordException extends Exception {
- private final MPUser user;
+ private final MPFileUser user;
- public IncorrectMasterPasswordException(final MPUser user) {
+ public MPIncorrectMasterPasswordException(final MPFileUser user) {
super( "Incorrect master password for user: " + user.getFullName() );
this.user = user;
}
- public MPUser getUser() {
+ public MPFileUser getUser() {
return user;
}
}
diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONMarshaller.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONMarshaller.java
index 537edffc..93194bdb 100644
--- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONMarshaller.java
+++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONMarshaller.java
@@ -18,7 +18,7 @@
package com.lyndir.masterpassword.model;
-import com.lyndir.masterpassword.MasterKey;
+import com.lyndir.masterpassword.MPMasterKey;
/**
@@ -27,7 +27,7 @@ import com.lyndir.masterpassword.MasterKey;
public class MPJSONMarshaller implements MPMarshaller {
@Override
- public String marshall(final MPUser user, final MasterKey masterKey, final ContentMode contentMode) {
+ public String marshall(final MPFileUser user, final MPMasterKey masterKey, final ContentMode contentMode) {
// TODO
return null;
}
diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONUnmarshaller.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONUnmarshaller.java
index 376700d9..8ced1481 100644
--- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONUnmarshaller.java
+++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONUnmarshaller.java
@@ -30,7 +30,7 @@ public class MPJSONUnmarshaller implements MPUnmarshaller {
@Nonnull
@Override
- public MPUser unmarshall(@Nonnull final File file)
+ public MPFileUser unmarshall(@Nonnull final File file)
throws IOException {
// TODO
return null;
@@ -38,7 +38,7 @@ public class MPJSONUnmarshaller implements MPUnmarshaller {
@Nonnull
@Override
- public MPUser unmarshall(@Nonnull final String content) {
+ public MPFileUser unmarshall(@Nonnull final String content) {
// TODO
return null;
}
diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPMarshaller.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPMarshaller.java
index f4195529..609f4ad8 100644
--- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPMarshaller.java
+++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPMarshaller.java
@@ -18,7 +18,8 @@
package com.lyndir.masterpassword.model;
-import com.lyndir.masterpassword.MasterKey;
+import com.lyndir.masterpassword.MPInvalidatedException;
+import com.lyndir.masterpassword.MPMasterKey;
/**
@@ -26,7 +27,8 @@ import com.lyndir.masterpassword.MasterKey;
*/
public interface MPMarshaller {
- String marshall(MPUser user, MasterKey masterKey, ContentMode contentMode);
+ String marshall(MPFileUser user, MPMasterKey masterKey, ContentMode contentMode)
+ throws MPInvalidatedException;
enum ContentMode {
PROTECTED( "Export of site names and stored passwords (unless device-private) encrypted with the master key." ),
diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPSite.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPSite.java
index ca9e56c3..9165b55d 100644
--- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPSite.java
+++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPSite.java
@@ -24,170 +24,44 @@ import com.google.common.primitives.UnsignedInteger;
import com.lyndir.masterpassword.*;
import java.util.Objects;
import javax.annotation.Nullable;
-import org.joda.time.Instant;
/**
- * @author lhunath, 14-12-05
+ * @author lhunath, 14-12-16
*/
-public class MPSite {
+public abstract class MPSite {
public static final UnsignedInteger DEFAULT_COUNTER = UnsignedInteger.ONE;
- private final MPUser user;
- private String siteName;
- @Nullable
- private String siteContent;
- private UnsignedInteger siteCounter;
- private MPResultType resultType;
- private MasterKey.Version algorithmVersion;
+ public abstract String getSiteName();
- @Nullable
- private String loginContent;
- @Nullable
- private MPResultType loginType;
+ public abstract void setSiteName(String siteName);
- @Nullable
- private String url;
- private int uses;
- private Instant lastUsed;
+ public abstract UnsignedInteger getSiteCounter();
- public MPSite(final MPUser user, final String siteName) {
- this( user, siteName, DEFAULT_COUNTER, MPResultType.DEFAULT );
+ public abstract void setSiteCounter(UnsignedInteger siteCounter);
+
+ public abstract MPResultType getResultType();
+
+ public abstract void setResultType(MPResultType resultType);
+
+ public abstract MPMasterKey.Version getAlgorithmVersion();
+
+ public abstract void setAlgorithmVersion(MPMasterKey.Version algorithmVersion);
+
+ public String resultFor(final MPMasterKey masterKey, final MPKeyPurpose keyPurpose, @Nullable final String keyContext,
+ @Nullable final String siteContent)
+ throws MPInvalidatedException {
+
+ return masterKey.siteResult(
+ getSiteName(), getSiteCounter(), keyPurpose, keyContext, getResultType(), siteContent, getAlgorithmVersion() );
}
- public MPSite(final MPUser user, final String siteName, final UnsignedInteger siteCounter, final MPResultType resultType) {
- this.user = user;
- this.siteName = siteName;
- this.siteCounter = siteCounter;
- this.resultType = resultType;
- this.algorithmVersion = MasterKey.Version.CURRENT;
- this.lastUsed = new Instant();
- }
+ public String loginFor(final MPMasterKey masterKey, final MPResultType loginType, @Nullable final String loginContent)
+ throws MPInvalidatedException {
- protected MPSite(final MPUser user, final String siteName, @Nullable final String siteContent, final UnsignedInteger siteCounter,
- final MPResultType resultType, final MasterKey.Version algorithmVersion,
- @Nullable final String loginContent, @Nullable final MPResultType loginType,
- @Nullable final String url, final int uses, final Instant lastUsed) {
- this.user = user;
- this.siteName = siteName;
- this.siteContent = siteContent;
- this.siteCounter = siteCounter;
- this.resultType = resultType;
- this.algorithmVersion = algorithmVersion;
- this.loginContent = loginContent;
- this.loginType = loginType;
- this.url = url;
- this.uses = uses;
- this.lastUsed = lastUsed;
- }
-
- public String resultFor(final MasterKey masterKey) {
- return resultFor( masterKey, MPKeyPurpose.Authentication, null );
- }
-
- public String resultFor(final MasterKey masterKey, final MPKeyPurpose purpose, @Nullable final String context) {
- return masterKey.siteResult( siteName, siteCounter, purpose, context, resultType, siteContent, algorithmVersion );
- }
-
- public String loginFor(final MasterKey masterKey) {
- if (loginType == null)
- loginType = MPResultType.GeneratedName;
-
- return masterKey.siteResult( siteName, DEFAULT_COUNTER, MPKeyPurpose.Identification, null, loginType, loginContent,
- algorithmVersion );
- }
-
- public MPUser getUser() {
- return user;
- }
-
- @Nullable
- protected String exportContent() {
- return null;
- }
-
- public MasterKey.Version getAlgorithmVersion() {
- return algorithmVersion;
- }
-
- public void setAlgorithmVersion(final MasterKey.Version mpVersion) {
- this.algorithmVersion = mpVersion;
- }
-
- public Instant getLastUsed() {
- return lastUsed;
- }
-
- public void updateLastUsed() {
- lastUsed = new Instant();
- user.updateLastUsed();
- }
-
- public String getSiteName() {
- return siteName;
- }
-
- public void setSiteName(final String siteName) {
- this.siteName = siteName;
- }
-
- @Nullable
- public String getSiteContent() {
- return siteContent;
- }
-
- public MPResultType getResultType() {
- return resultType;
- }
-
- public void setResultType(final MPResultType resultType) {
- this.resultType = resultType;
- }
-
- public UnsignedInteger getSiteCounter() {
- return siteCounter;
- }
-
- public void setSiteCounter(final UnsignedInteger siteCounter) {
- this.siteCounter = siteCounter;
- }
-
- public int getUses() {
- return uses;
- }
-
- public void setUses(final int uses) {
- this.uses = uses;
- }
-
- @Nullable
- public MPResultType getLoginType() {
- return loginType;
- }
-
- @Nullable
- public String getLoginContent() {
- return loginContent;
- }
-
- public void setLoginName(final MasterKey masterKey, @Nullable final MPResultType loginType, @Nullable final String result) {
- this.loginType = loginType;
- if (this.loginType != null)
- if (result == null)
- this.loginContent = null;
- else
- this.loginContent = masterKey.siteState(
- siteName, DEFAULT_COUNTER, MPKeyPurpose.Identification, null, this.loginType, result, algorithmVersion );
- }
-
- @Nullable
- public String getUrl() {
- return url;
- }
-
- public void setUrl(@Nullable final String url) {
- this.url = url;
+ return masterKey.siteResult(
+ getSiteName(), DEFAULT_COUNTER, MPKeyPurpose.Identification, null, loginType, loginContent, getAlgorithmVersion() );
}
@Override
@@ -202,6 +76,6 @@ public class MPSite {
@Override
public String toString() {
- return strf( "{MPSite: %s}", getSiteName() );
+ return strf( "{%s: %s}", getClass().getSimpleName(), getSiteName() );
}
}
diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPSiteResult.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPSiteResult.java
index 956bef3e..646a0f21 100644
--- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPSiteResult.java
+++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPSiteResult.java
@@ -28,13 +28,13 @@ import java.util.Objects;
*/
public class MPSiteResult {
- private final MPSite site;
+ private final MPFileSite site;
- public MPSiteResult(final MPSite site) {
+ public MPSiteResult(final MPFileSite site) {
this.site = site;
}
- public MPSite getSite() {
+ public MPFileSite getSite() {
return site;
}
diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUnmarshaller.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUnmarshaller.java
index 3b0dec65..97a966a1 100644
--- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUnmarshaller.java
+++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUnmarshaller.java
@@ -28,9 +28,9 @@ import javax.annotation.Nonnull;
public interface MPUnmarshaller {
@Nonnull
- MPUser unmarshall(@Nonnull File file)
+ MPFileUser unmarshall(@Nonnull File file)
throws IOException;
@Nonnull
- MPUser unmarshall(@Nonnull String content);
+ MPFileUser unmarshall(@Nonnull String content);
}
diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUser.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUser.java
index 00e7a76a..87c0bdde 100755
--- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUser.java
+++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUser.java
@@ -18,155 +18,69 @@
package com.lyndir.masterpassword.model;
-import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
+import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Sets;
+import com.google.common.base.Preconditions;
import com.lyndir.lhunath.opal.system.CodeUtils;
-import com.lyndir.masterpassword.MPResultType;
-import com.lyndir.masterpassword.MasterKey;
+import com.lyndir.masterpassword.MPInvalidatedException;
+import com.lyndir.masterpassword.MPMasterKey;
import java.util.*;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import org.joda.time.*;
/**
- * @author lhunath, 14-12-07
+ * @author lhunath, 2014-06-08
*/
-public class MPUser implements Comparable {
-
- private final String fullName;
- private final Collection sites = Sets.newHashSet();
+public abstract class MPUser {
@Nullable
- private byte[] keyID;
- private MasterKey.Version algorithmVersion;
+ protected MPMasterKey key;
- private int avatar;
- private MPResultType defaultType;
- private ReadableInstant lastUsed;
+ public abstract String getFullName();
- public MPUser(final String fullName) {
- this( fullName, null, MasterKey.Version.CURRENT );
+ public boolean isMasterKeyAvailable() {
+ return key != null;
}
- public MPUser(final String fullName, @Nullable final byte[] keyID, final MasterKey.Version algorithmVersion) {
- this( fullName, keyID, algorithmVersion, 0, MPResultType.DEFAULT, new Instant() );
- }
-
- public MPUser(final String fullName, @Nullable final byte[] keyID, final MasterKey.Version algorithmVersion, final int avatar,
- final MPResultType defaultType, final ReadableInstant lastUsed) {
- this.fullName = fullName;
- this.keyID = (keyID == null)? null: keyID.clone();
- this.algorithmVersion = algorithmVersion;
- this.avatar = avatar;
- this.defaultType = defaultType;
- this.lastUsed = lastUsed;
- }
-
- public Collection findSitesByName(final String query) {
- ImmutableList.Builder results = ImmutableList.builder();
- for (final MPSite site : getSites())
- if (site.getSiteName().startsWith( query ))
- results.add( new MPSiteResult( site ) );
-
- return results.build();
- }
-
- public void addSite(final MPSite site) {
- sites.add( site );
- }
-
- public void deleteSite(final MPSite site) {
- sites.remove( site );
- }
-
- public String getFullName() {
- return fullName;
- }
-
- public boolean hasKeyID() {
- return keyID != null;
- }
-
- public String exportKeyID() {
- return CodeUtils.encodeHex( keyID );
- }
-
- /**
- * Performs an authentication attempt against the keyID for this user.
- *
- * Note: If this user doesn't have a keyID set yet, authentication will always succeed and the key ID will be set as a result.
- *
- * @param masterPassword The password to authenticate with.
- *
- * @return The master key for the user if authentication was successful.
- *
- * @throws IncorrectMasterPasswordException If authentication fails due to the given master password not matching the user's keyID.
- */
@Nonnull
- @SuppressWarnings("MethodCanBeVariableArityMethod")
- public MasterKey authenticate(final char[] masterPassword)
- throws IncorrectMasterPasswordException {
- MasterKey masterKey = new MasterKey( getFullName(), masterPassword );
- if ((keyID == null) || (keyID.length == 0))
- keyID = masterKey.getKeyID( algorithmVersion );
- else if (!Arrays.equals( masterKey.getKeyID( algorithmVersion ), keyID ))
- throw new IncorrectMasterPasswordException( this );
-
- return masterKey;
+ public MPMasterKey getMasterKey() {
+ return Preconditions.checkNotNull( key, "User is not authenticated: " + getFullName() );
}
+ public String exportKeyID()
+ throws MPInvalidatedException {
+ return CodeUtils.encodeHex( getMasterKey().getKeyID( getAlgorithmVersion() ) );
+ }
+
+ public abstract MPMasterKey.Version getAlgorithmVersion();
+
public int getAvatar() {
- return avatar;
+ return 0;
}
- public void setAvatar(final int avatar) {
- this.avatar = avatar;
- }
+ public abstract void addSite(S site);
- public MPResultType getDefaultType() {
- return defaultType;
- }
+ public abstract void deleteSite(S site);
- public void setDefaultType(final MPResultType defaultType) {
- this.defaultType = defaultType;
- }
+ public abstract Collection findSites(String query);
- public ReadableInstant getLastUsed() {
- return lastUsed;
- }
+ @Nonnull
+ public abstract MPMasterKey authenticate(char[] masterPassword)
+ throws MPIncorrectMasterPasswordException;
- public void updateLastUsed() {
- lastUsed = new Instant();
- }
-
- public Iterable getSites() {
- return sites;
+ @Override
+ public int hashCode() {
+ return Objects.hashCode( getFullName() );
}
@Override
public boolean equals(final Object obj) {
- return (this == obj) || ((obj instanceof MPUser) && Objects.equals( fullName, ((MPUser) obj).fullName ));
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode( fullName );
+ return (this == obj) || ((obj instanceof MPUser) && Objects.equals( getFullName(), ((MPUser>) obj).getFullName() ));
}
@Override
public String toString() {
- return strf( "{MPUser: %s}", fullName );
- }
-
- @Override
- public int compareTo(final MPUser o) {
- int comparison = lastUsed.compareTo( o.lastUsed );
- if (comparison == 0)
- comparison = fullName.compareTo( o.fullName );
-
- return comparison;
+ return strf( "{%s: %s}", getClass().getSimpleName(), getFullName() );
}
}
diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUserManager.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUserManager.java
index b36b819c..8ff40074 100644
--- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUserManager.java
+++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUserManager.java
@@ -19,6 +19,7 @@
package com.lyndir.masterpassword.model;
import com.google.common.collect.*;
+import com.lyndir.masterpassword.MPInvalidatedException;
import java.util.*;
@@ -27,31 +28,31 @@ import java.util.*;
*/
public abstract class MPUserManager {
- private final Map usersByName = Maps.newHashMap();
+ private final Map usersByName = Maps.newHashMap();
static MPUserManager instance;
public static MPUserManager get() {
return instance;
}
- protected MPUserManager(final Iterable users) {
- for (final MPUser user : users)
+ protected MPUserManager(final Iterable users) {
+ for (final MPFileUser user : users)
usersByName.put( user.getFullName(), user );
}
- public SortedSet getUsers() {
+ public SortedSet getUsers() {
return FluentIterable.from( usersByName.values() ).toSortedSet( Ordering.natural() );
}
- public MPUser getUserNamed(final String fullName) {
+ public MPFileUser getUserNamed(final String fullName) {
return usersByName.get( fullName );
}
- public void addUser(final MPUser user) {
+ public void addUser(final MPFileUser user) {
usersByName.put( user.getFullName(), user );
}
- public void deleteUser(final MPUser user) {
+ public void deleteUser(final MPFileUser user) {
usersByName.remove( user.getFullName() );
}
}
diff --git a/core/java/tests/src/main/java/com/lyndir/masterpassword/MPTestSuite.java b/core/java/tests/src/main/java/com/lyndir/masterpassword/MPTestSuite.java
index 7036b75b..9db579d9 100644
--- a/core/java/tests/src/main/java/com/lyndir/masterpassword/MPTestSuite.java
+++ b/core/java/tests/src/main/java/com/lyndir/masterpassword/MPTestSuite.java
@@ -23,12 +23,10 @@ import com.google.common.collect.Lists;
import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
-import com.lyndir.lhunath.opal.system.util.NNFunctionNN;
import java.io.IOException;
import java.util.Deque;
import java.util.List;
import java.util.concurrent.Callable;
-import javax.annotation.Nonnull;
import javax.xml.parsers.*;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
@@ -38,7 +36,7 @@ import org.xml.sax.ext.DefaultHandler2;
/**
* @author lhunath, 2015-12-22
*/
-@SuppressWarnings("HardCodedStringLiteral")
+@SuppressWarnings({ "HardCodedStringLiteral", "ProhibitedExceptionDeclared" })
public class MPTestSuite implements Callable {
@SuppressWarnings("UnusedDeclaration")
@@ -134,7 +132,8 @@ public class MPTestSuite implements Callable {
return tests;
}
- public boolean forEach(final String testName, final NNFunctionNN testFunction) {
+ public boolean forEach(final String testName, final TestCase testFunction)
+ throws Exception {
List cases = tests.getCases();
for (int c = 0; c < cases.size(); c++) {
MPTests.Case testCase = cases.get( c );
@@ -144,7 +143,7 @@ public class MPTestSuite implements Callable {
progress( Logger.Target.INFO, c, cases.size(), //
"[%s] on %s...", testName, testCase.getIdentifier() );
- if (!testFunction.apply( testCase )) {
+ if (!testFunction.run( testCase )) {
progress( Logger.Target.ERROR, cases.size(), cases.size(), //
"[%s] on %s: FAILED!", testName, testCase.getIdentifier() );
@@ -168,11 +167,11 @@ public class MPTestSuite implements Callable {
@Override
public Boolean call()
throws Exception {
- return forEach( "mpw", new NNFunctionNN() {
- @Nonnull
+ return forEach( "mpw", new TestCase() {
@Override
- public Boolean apply(@Nonnull final MPTests.Case testCase) {
- MasterKey masterKey = new MasterKey( testCase.getFullName(), testCase.getMasterPassword() );
+ public boolean run(final MPTests.Case testCase)
+ throws Exception {
+ MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), testCase.getMasterPassword() );
String sitePassword = masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
testCase.getKeyContext(), testCase.getResultType(),
null, testCase.getAlgorithm() );
@@ -196,4 +195,11 @@ public class MPTestSuite implements Callable {
void progress(int current, int max, String messageFormat, Object... args);
}
+
+
+ public interface TestCase {
+
+ boolean run(MPTests.Case testCase)
+ throws Exception;
+ }
}
diff --git a/core/java/tests/src/main/java/com/lyndir/masterpassword/MPTests.java b/core/java/tests/src/main/java/com/lyndir/masterpassword/MPTests.java
index 1c79c157..c2e5dc9e 100644
--- a/core/java/tests/src/main/java/com/lyndir/masterpassword/MPTests.java
+++ b/core/java/tests/src/main/java/com/lyndir/masterpassword/MPTests.java
@@ -171,8 +171,8 @@ public class MPTests {
}
@Nonnull
- public MasterKey.Version getAlgorithm() {
- return MasterKey.Version.fromInt( checkNotNull( algorithm ) );
+ public MPMasterKey.Version getAlgorithm() {
+ return MPMasterKey.Version.fromInt( checkNotNull( algorithm ) );
}
@Nonnull
diff --git a/core/java/tests/src/main/resources/mpw_tests.xml b/core/java/tests/src/main/resources/mpw_tests.xml
deleted file mode 100644
index 01f56a9a..00000000
--- a/core/java/tests/src/main/resources/mpw_tests.xml
+++ /dev/null
@@ -1,279 +0,0 @@
-
-
-
- -1
- Robert Lee Mitchell
- banana colored duckling
- 98EEF4D1DF46D849574A82A03C3177056B15DFFCA29BB3899DE4628453675302
- masterpasswordapp.com
- 1
- Long
- Authentication
-
-
-
-
-
- 3
- Jejr5[RepuSosp
-
-
- ⛄
- 1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8
- NopaDajh8=Fene
-
-
- ⛄
- 351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08
- QesuHirv5-Xepl
-
-
- ⛄
- LiheCuwhSerz6)
-
-
- Identification
- Name
- wohzaqage
-
-
- Recovery
- Phrase
- xin diyjiqoja hubu
-
-
- question
- xogx tem cegyiva jab
-
-
- Maximum
- W6@692^B1#&@gVdSdLZ@
-
-
- Medium
- Jej2$Quv
-
-
- Basic
- WAo2xIg6
-
-
- Short
- Jej2
-
-
- PIN
- 7662
-
-
- Name
- jejraquvo
-
-
- Phrase
- jejr quv cabsibu tam
-
-
- 4294967295
- XambHoqo6[Peni
-
-
-
-
- 2
- Jejr5[RepuSosp
-
-
- ⛄
- 1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8
- WaqoGuho2[Xaxw
-
-
- ⛄
- 351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08
- QesuHirv5-Xepl
-
-
- ⛄
- LiheCuwhSerz6)
-
-
- Identification
- Name
- wohzaqage
-
-
- Recovery
- Phrase
- xin diyjiqoja hubu
-
-
- question
- xogx tem cegyiva jab
-
-
- Maximum
- W6@692^B1#&@gVdSdLZ@
-
-
- Medium
- Jej2$Quv
-
-
- Basic
- WAo2xIg6
-
-
- Short
- Jej2
-
-
- PIN
- 7662
-
-
- Name
- jejraquvo
-
-
- Phrase
- jejr quv cabsibu tam
-
-
- 4294967295
- XambHoqo6[Peni
-
-
-
-
- 1
- Jejr5[RepuSosp
-
-
- ⛄
- 1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8
- WaqoGuho2[Xaxw
-
-
- ⛄
- 351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08
- QesuHirv5-Xepl
-
-
- ⛄
- WawiYarp2@Kodh
-
-
- Identification
- Name
- wohzaqage
-
-
- Recovery
- Phrase
- xin diyjiqoja hubu
-
-
- question
- xogx tem cegyiva jab
-
-
- Maximum
- W6@692^B1#&@gVdSdLZ@
-
-
- Medium
- Jej2$Quv
-
-
- Basic
- WAo2xIg6
-
-
- Short
- Jej2
-
-
- PIN
- 7662
-
-
- Name
- jejraquvo
-
-
- Phrase
- jejr quv cabsibu tam
-
-
- 4294967295
- XambHoqo6[Peni
-
-
-
-
- 0
- Feji5@ReduWosh
-
-
- ⛄
- 1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8
- HajrYudo7@Mamh
-
-
- ⛄
- 351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08
- MewmDini0]Meho
-
-
- ⛄
- HahiVana2@Nole
-
-
- Identification
- Name
- lozwajave
-
-
- Recovery
- Phrase
- miy lirfijoja dubu
-
-
- question
- movm bex gevrica jaf
-
-
- Maximum
- w1!3bA3icmRAc)SS@lwl
-
-
- Medium
- Fej7]Jug
-
-
- Basic
- wvH7irC1
-
-
- Short
- Fej7
-
-
- PIN
- 2117
-
-
- Name
- fejrajugo
-
-
- Phrase
- fejr jug gabsibu bax
-
-
- 4294967295
- QateDojh1@Hecn
-
-
-
diff --git a/core/java/tests/src/main/resources/mpw_tests.xml b/core/java/tests/src/main/resources/mpw_tests.xml
new file mode 120000
index 00000000..0176543d
--- /dev/null
+++ b/core/java/tests/src/main/resources/mpw_tests.xml
@@ -0,0 +1 @@
+../../../../../mpw_tests.xml
\ No newline at end of file
diff --git a/core/java/tests/src/test/java/com/lyndir/masterpassword/MasterKeyTest.java b/core/java/tests/src/test/java/com/lyndir/masterpassword/MPMasterKeyTest.java
similarity index 74%
rename from core/java/tests/src/test/java/com/lyndir/masterpassword/MasterKeyTest.java
rename to core/java/tests/src/test/java/com/lyndir/masterpassword/MPMasterKeyTest.java
index 1063d116..a9b476ce 100644
--- a/core/java/tests/src/test/java/com/lyndir/masterpassword/MasterKeyTest.java
+++ b/core/java/tests/src/test/java/com/lyndir/masterpassword/MPMasterKeyTest.java
@@ -22,17 +22,15 @@ import static org.testng.Assert.*;
import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger;
-import com.lyndir.lhunath.opal.system.util.NNFunctionNN;
-import javax.annotation.Nonnull;
import org.jetbrains.annotations.NonNls;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
-public class MasterKeyTest {
+public class MPMasterKeyTest {
@SuppressWarnings("UnusedDeclaration")
- private static final Logger logger = Logger.get( MasterKeyTest.class );
+ private static final Logger logger = Logger.get( MPMasterKeyTest.class );
@NonNls
private MPTestSuite testSuite;
@@ -48,11 +46,11 @@ public class MasterKeyTest {
public void testEncode()
throws Exception {
- testSuite.forEach( "testEncode", new NNFunctionNN() {
- @Nonnull
+ testSuite.forEach( "testEncode", new MPTestSuite.TestCase() {
@Override
- public Boolean apply(@Nonnull final MPTests.Case testCase) {
- MasterKey masterKey = new MasterKey( testCase.getFullName(), testCase.getMasterPassword() );
+ public boolean run(final MPTests.Case testCase)
+ throws Exception {
+ MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), testCase.getMasterPassword() );
assertEquals(
masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
@@ -71,7 +69,7 @@ public class MasterKeyTest {
MPTests.Case defaultCase = testSuite.getTests().getDefaultCase();
- assertEquals( new MasterKey( defaultCase.getFullName(), defaultCase.getMasterPassword() ).getFullName(),
+ assertEquals( new MPMasterKey( defaultCase.getFullName(), defaultCase.getMasterPassword() ).getFullName(),
defaultCase.getFullName(), "[testGetUserName] Failed test case: " + defaultCase );
}
@@ -79,11 +77,11 @@ public class MasterKeyTest {
public void testGetKeyID()
throws Exception {
- testSuite.forEach( "testGetKeyID", new NNFunctionNN() {
- @Nonnull
+ testSuite.forEach( "testGetKeyID", new MPTestSuite.TestCase() {
@Override
- public Boolean apply(@Nonnull final MPTests.Case testCase) {
- MasterKey masterKey = new MasterKey( testCase.getFullName(), testCase.getMasterPassword() );
+ public boolean run(final MPTests.Case testCase)
+ throws Exception {
+ MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), testCase.getMasterPassword() );
assertEquals( CodeUtils.encodeHex( masterKey.getKeyID( testCase.getAlgorithm() ) ),
testCase.getKeyID(), "[testGetKeyID] Failed test case: " + testCase );
diff --git a/platform-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java b/platform-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java
index 4ad71dba..cb3f35d9 100644
--- a/platform-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java
+++ b/platform-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java
@@ -51,12 +51,12 @@ public class EmergencyActivity extends Activity {
private static final int PASSWORD_NOTIFICATION = 0;
public static final int CLIPBOARD_CLEAR_DELAY = 20 /* s */ * MPConstant.MS_PER_S;
- private final Preferences preferences = Preferences.get( this );
- private final ListeningExecutorService executor = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() );
- private final ImmutableList allResultTypes = ImmutableList.copyOf( MPResultType.forClass( MPResultTypeClass.Template ) );
- private final ImmutableList allVersions = ImmutableList.copyOf( MasterKey.Version.values() );
+ private final Preferences preferences = Preferences.get( this );
+ private final ListeningExecutorService executor = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() );
+ private final ImmutableList allResultTypes = ImmutableList.copyOf( MPResultType.forClass( MPResultTypeClass.Template ) );
+ private final ImmutableList allVersions = ImmutableList.copyOf( MPMasterKey.Version.values() );
- private MasterKey masterKey;
+ private MPMasterKey masterKey;
@BindView(R.id.progressView)
ProgressBar progressView;
@@ -154,7 +154,7 @@ public class EmergencyActivity extends Activity {
@Override
public void onClick(final View v) {
@SuppressWarnings("SuspiciousMethodCalls")
- MasterKey.Version siteVersion =
+ MPMasterKey.Version siteVersion =
allVersions.get( (allVersions.indexOf( siteVersionButton.getTag() ) + 1) % allVersions.size() );
preferences.setDefaultVersion( siteVersion );
siteVersionButton.setTag( siteVersion );
@@ -221,7 +221,7 @@ public class EmergencyActivity extends Activity {
MPResultType defaultResultType = preferences.getDefaultResultType();
resultTypeButton.setTag( defaultResultType );
resultTypeButton.setText( defaultResultType.getShortName() );
- MasterKey.Version defaultVersion = preferences.getDefaultVersion();
+ MPMasterKey.Version defaultVersion = preferences.getDefaultVersion();
siteVersionButton.setTag( defaultVersion );
siteVersionButton.setText( defaultVersion.name() );
siteCounterButton.setText( MessageFormat.format( "{0}", 1 ) );
@@ -275,15 +275,15 @@ public class EmergencyActivity extends Activity {
sitePasswordField.setText( "" );
progressView.setVisibility( View.VISIBLE );
- masterKey = new MasterKey( fullName, masterPassword );
+ masterKey = new MPMasterKey( fullName, masterPassword );
updateSitePassword();
}
private void updateSitePassword() {
- final String siteName = siteNameField.getText().toString();
- final MPResultType type = (MPResultType) resultTypeButton.getTag();
- final UnsignedInteger counter = UnsignedInteger.valueOf( siteCounterButton.getText().toString() );
- final MasterKey.Version version = (MasterKey.Version) siteVersionButton.getTag();
+ final String siteName = siteNameField.getText().toString();
+ final MPResultType type = (MPResultType) resultTypeButton.getTag();
+ final UnsignedInteger counter = UnsignedInteger.valueOf( siteCounterButton.getText().toString() );
+ final MPMasterKey.Version version = (MPMasterKey.Version) siteVersionButton.getTag();
if ((masterKey == null) || siteName.isEmpty() || (type == null)) {
sitePasswordField.setText( "" );
@@ -310,6 +310,10 @@ public class EmergencyActivity extends Activity {
}
} );
}
+ catch (final MPInvalidatedException ignored) {
+ sitePasswordField.setText( "" );
+ progressView.setVisibility( View.INVISIBLE );
+ }
catch (final RuntimeException e) {
sitePasswordField.setText( "" );
progressView.setVisibility( View.INVISIBLE );
diff --git a/platform-android/src/main/java/com/lyndir/masterpassword/Preferences.java b/platform-android/src/main/java/com/lyndir/masterpassword/Preferences.java
index 7e8801d6..53f10d6a 100644
--- a/platform-android/src/main/java/com/lyndir/masterpassword/Preferences.java
+++ b/platform-android/src/main/java/com/lyndir/masterpassword/Preferences.java
@@ -151,7 +151,7 @@ public final class Preferences {
return MPResultType.values()[prefs().getInt( PREF_RESULT_TYPE, MPResultType.DEFAULT.ordinal() )];
}
- public boolean setDefaultVersion(final MasterKey.Version value) {
+ public boolean setDefaultVersion(final MPMasterKey.Version value) {
if (getDefaultVersion() == value)
return false;
@@ -160,7 +160,7 @@ public final class Preferences {
}
@Nonnull
- public MasterKey.Version getDefaultVersion() {
- return MasterKey.Version.values()[prefs().getInt( PREF_ALGORITHM_VERSION, MasterKey.Version.CURRENT.ordinal() )];
+ public MPMasterKey.Version getDefaultVersion() {
+ return MPMasterKey.Version.values()[prefs().getInt( PREF_ALGORITHM_VERSION, MPMasterKey.Version.CURRENT.ordinal() )];
}
}
diff --git a/platform-independent/cli-c/mpw_tests.xml b/platform-independent/cli-c/mpw_tests.xml
index 7a2509be..89a4ee29 120000
--- a/platform-independent/cli-c/mpw_tests.xml
+++ b/platform-independent/cli-c/mpw_tests.xml
@@ -1 +1 @@
-../../core/java/tests/src/main/resources/mpw_tests.xml
\ No newline at end of file
+../../core/mpw_tests.xml
\ No newline at end of file
diff --git a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/GUI.java b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/GUI.java
index bdfe7fb8..ffd8582e 100644
--- a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/GUI.java
+++ b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/GUI.java
@@ -24,7 +24,6 @@ import com.google.common.io.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.TypeUtils;
-import com.lyndir.masterpassword.gui.model.User;
import com.lyndir.masterpassword.gui.view.PasswordFrame;
import com.lyndir.masterpassword.gui.view.UnlockFrame;
import java.io.*;
@@ -46,7 +45,7 @@ public class GUI implements UnlockFrame.SignInCallback {
private static final Logger logger = Logger.get( GUI.class );
private final UnlockFrame unlockFrame = new UnlockFrame( this );
- private PasswordFrame passwordFrame;
+ private PasswordFrame, ?> passwordFrame;
public static void main(final String... args) {
@@ -104,12 +103,8 @@ public class GUI implements UnlockFrame.SignInCallback {
}
@Override
- public void signedIn(final User user) {
- passwordFrame = newPasswordFrame( user );
+ public void signedIn(final PasswordFrame, ?> passwordFrame) {
+ this.passwordFrame = passwordFrame;
open();
}
-
- protected PasswordFrame newPasswordFrame(final User user) {
- return new PasswordFrame( user );
- }
}
diff --git a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/IncognitoSite.java b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/IncognitoSite.java
index 5b5d333d..4140851d 100644
--- a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/IncognitoSite.java
+++ b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/IncognitoSite.java
@@ -20,21 +20,22 @@ package com.lyndir.masterpassword.gui.model;
import com.google.common.primitives.UnsignedInteger;
import com.lyndir.masterpassword.MPResultType;
-import com.lyndir.masterpassword.MasterKey;
+import com.lyndir.masterpassword.MPMasterKey;
+import com.lyndir.masterpassword.model.MPSite;
/**
* @author lhunath, 14-12-16
*/
-public class IncognitoSite extends Site {
+public class IncognitoSite extends MPSite {
- private String siteName;
- private UnsignedInteger siteCounter;
- private MPResultType resultType;
- private MasterKey.Version algorithmVersion;
+ private String siteName;
+ private UnsignedInteger siteCounter;
+ private MPResultType resultType;
+ private MPMasterKey.Version algorithmVersion;
public IncognitoSite(final String siteName, final UnsignedInteger siteCounter, final MPResultType resultType,
- final MasterKey.Version algorithmVersion) {
+ final MPMasterKey.Version algorithmVersion) {
this.siteName = siteName;
this.siteCounter = siteCounter;
this.resultType = resultType;
@@ -62,12 +63,12 @@ public class IncognitoSite extends Site {
}
@Override
- public MasterKey.Version getAlgorithmVersion() {
+ public MPMasterKey.Version getAlgorithmVersion() {
return algorithmVersion;
}
@Override
- public void setAlgorithmVersion(final MasterKey.Version algorithmVersion) {
+ public void setAlgorithmVersion(final MPMasterKey.Version algorithmVersion) {
this.algorithmVersion = algorithmVersion;
}
diff --git a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/IncognitoUser.java b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/IncognitoUser.java
index 0feae51f..1aaa6216 100755
--- a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/IncognitoUser.java
+++ b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/IncognitoUser.java
@@ -19,15 +19,17 @@
package com.lyndir.masterpassword.gui.model;
import com.google.common.collect.ImmutableList;
-import com.lyndir.masterpassword.MasterKey;
-import com.lyndir.masterpassword.model.IncorrectMasterPasswordException;
-import javax.annotation.Nullable;
+import com.lyndir.masterpassword.MPMasterKey;
+import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException;
+import com.lyndir.masterpassword.model.MPUser;
+import java.util.Collection;
+import javax.annotation.Nonnull;
/**
* @author lhunath, 2014-06-08
*/
-public class IncognitoUser extends User {
+public class IncognitoUser extends MPUser {
private final String fullName;
@@ -41,21 +43,27 @@ public class IncognitoUser extends User {
}
@Override
- public void authenticate(final char[] masterPassword)
- throws IncorrectMasterPasswordException {
- this.key = new MasterKey( getFullName(), masterPassword );
+ public MPMasterKey.Version getAlgorithmVersion() {
+ return MPMasterKey.Version.CURRENT;
}
@Override
- public Iterable findSitesByName(final String siteName) {
+ public void addSite(final IncognitoSite site) {
+ }
+
+ @Override
+ public void deleteSite(final IncognitoSite site) {
+ }
+
+ @Override
+ public Collection findSites(final String query) {
return ImmutableList.of();
}
+ @Nonnull
@Override
- public void addSite(final Site site) {
- }
-
- @Override
- public void deleteSite(final Site site) {
+ public MPMasterKey authenticate(final char[] masterPassword)
+ throws MPIncorrectMasterPasswordException {
+ return key = new MPMasterKey( getFullName(), masterPassword );
}
}
diff --git a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/ModelSite.java b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/ModelSite.java
deleted file mode 100755
index 08ed1a8d..00000000
--- a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/ModelSite.java
+++ /dev/null
@@ -1,96 +0,0 @@
-//==============================================================================
-// This file is part of Master Password.
-// Copyright (c) 2011-2017, Maarten Billemont.
-//
-// Master Password is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Master Password is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You can find a copy of the GNU General Public License in the
-// LICENSE file. Alternatively, see .
-//==============================================================================
-
-package com.lyndir.masterpassword.gui.model;
-
-import com.google.common.primitives.UnsignedInteger;
-import com.lyndir.masterpassword.MPResultType;
-import com.lyndir.masterpassword.MasterKey;
-import com.lyndir.masterpassword.model.*;
-
-
-/**
- * @author lhunath, 14-12-16
- */
-public class ModelSite extends Site {
-
- private final MPSite model;
-
- public ModelSite(final MPSiteResult result) {
- model = result.getSite();
- }
-
- public MPSite getModel() {
- return model;
- }
-
- @Override
- public String getSiteName() {
- return model.getSiteName();
- }
-
- @Override
- public void setSiteName(final String siteName) {
- model.setSiteName( siteName );
- MPUserFileManager.get().save();
- }
-
- @Override
- public MPResultType getResultType() {
- return model.getResultType();
- }
-
- @Override
- public void setResultType(final MPResultType resultType) {
- if (resultType != getResultType()) {
- model.setResultType( resultType );
- MPUserFileManager.get().save();
- }
- }
-
- @Override
- public MasterKey.Version getAlgorithmVersion() {
- return model.getAlgorithmVersion();
- }
-
- @Override
- public void setAlgorithmVersion(final MasterKey.Version algorithmVersion) {
- if (algorithmVersion != getAlgorithmVersion()) {
- model.setAlgorithmVersion( algorithmVersion );
- MPUserFileManager.get().save();
- }
- }
-
- @Override
- public UnsignedInteger getSiteCounter() {
- return model.getSiteCounter();
- }
-
- @Override
- public void setSiteCounter(final UnsignedInteger siteCounter) {
- if (siteCounter.equals( getSiteCounter() )) {
- model.setSiteCounter( siteCounter );
- MPUserFileManager.get().save();
- }
- }
-
- public void use() {
- model.updateLastUsed();
- MPUserFileManager.get().save();
- }
-}
diff --git a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/ModelUser.java b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/ModelUser.java
deleted file mode 100755
index 094830d5..00000000
--- a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/ModelUser.java
+++ /dev/null
@@ -1,97 +0,0 @@
-//==============================================================================
-// This file is part of Master Password.
-// Copyright (c) 2011-2017, Maarten Billemont.
-//
-// Master Password is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Master Password is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You can find a copy of the GNU General Public License in the
-// LICENSE file. Alternatively, see .
-//==============================================================================
-
-package com.lyndir.masterpassword.gui.model;
-
-import com.google.common.base.Function;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.FluentIterable;
-import com.lyndir.masterpassword.gui.*;
-import com.lyndir.masterpassword.model.*;
-import java.util.Arrays;
-import javax.annotation.Nullable;
-
-
-/**
- * @author lhunath, 14-12-08
- */
-public class ModelUser extends User {
-
- private final MPUser model;
-
- public ModelUser(final MPUser model) {
- this.model = model;
- }
-
- public MPUser getModel() {
- return model;
- }
-
- @Override
- public String getFullName() {
- return model.getFullName();
- }
-
- @Override
- public int getAvatar() {
- return model.getAvatar();
- }
-
- public void setAvatar(final int avatar) {
- model.setAvatar( avatar % Res.avatars());
- MPUserFileManager.get().save();
- }
-
- @Override
- public void authenticate(final char[] masterPassword)
- throws IncorrectMasterPasswordException {
- key = model.authenticate( masterPassword );
- MPUserFileManager.get().save();
- }
-
- @Override
- public Iterable findSitesByName(final String siteName) {
- return FluentIterable.from( model.findSitesByName( siteName ) ).transform( new Function() {
- @Nullable
- @Override
- public Site apply(@Nullable final MPSiteResult site) {
- return new ModelSite( Preconditions.checkNotNull( site ) );
- }
- } );
- }
-
- @Override
- public void addSite(final Site site) {
- model.addSite( new MPSite( model, site.getSiteName(), site.getSiteCounter(), site.getResultType() ) );
- model.updateLastUsed();
- MPUserFileManager.get().save();
- }
-
- @Override
- public void deleteSite(final Site site) {
- if (site instanceof ModelSite) {
- model.deleteSite(((ModelSite) site).getModel());
- MPUserFileManager.get().save();
- }
- }
-
- public boolean keySaved() {
- // TODO
- return false;
- }
-}
diff --git a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/Site.java b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/Site.java
deleted file mode 100644
index 454778de..00000000
--- a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/Site.java
+++ /dev/null
@@ -1,53 +0,0 @@
-//==============================================================================
-// This file is part of Master Password.
-// Copyright (c) 2011-2017, Maarten Billemont.
-//
-// Master Password is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Master Password is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You can find a copy of the GNU General Public License in the
-// LICENSE file. Alternatively, see .
-//==============================================================================
-
-package com.lyndir.masterpassword.gui.model;
-
-import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
-
-import com.google.common.primitives.UnsignedInteger;
-import com.lyndir.masterpassword.MPResultType;
-import com.lyndir.masterpassword.MasterKey;
-
-
-/**
- * @author lhunath, 14-12-16
- */
-public abstract class Site {
-
- public abstract String getSiteName();
-
- public abstract void setSiteName(String siteName);
-
- public abstract MPResultType getResultType();
-
- public abstract void setResultType(MPResultType resultType);
-
- public abstract MasterKey.Version getAlgorithmVersion();
-
- public abstract void setAlgorithmVersion(MasterKey.Version algorithmVersion);
-
- public abstract UnsignedInteger getSiteCounter();
-
- public abstract void setSiteCounter(UnsignedInteger siteCounter);
-
- @Override
- public String toString() {
- return strf( "{%s: %s}", getClass().getSimpleName(), getSiteName() );
- }
-}
diff --git a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/User.java b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/User.java
deleted file mode 100755
index 4cc1011a..00000000
--- a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/model/User.java
+++ /dev/null
@@ -1,76 +0,0 @@
-//==============================================================================
-// This file is part of Master Password.
-// Copyright (c) 2011-2017, Maarten Billemont.
-//
-// Master Password is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Master Password is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You can find a copy of the GNU General Public License in the
-// LICENSE file. Alternatively, see .
-//==============================================================================
-
-package com.lyndir.masterpassword.gui.model;
-
-import com.google.common.base.Preconditions;
-import com.lyndir.masterpassword.MasterKey;
-import com.lyndir.masterpassword.model.IncorrectMasterPasswordException;
-import java.util.*;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-
-/**
- * @author lhunath, 2014-06-08
- */
-public abstract class User {
-
- @Nullable
- protected MasterKey key;
-
- public abstract String getFullName();
-
- @SuppressWarnings("MethodCanBeVariableArityMethod")
- public abstract void authenticate(char[] masterPassword)
- throws IncorrectMasterPasswordException;
-
- public int getAvatar() {
- return 0;
- }
-
- public boolean isKeyAvailable() {
- return key != null;
- }
-
- @Nonnull
- public MasterKey getKey() {
- return Preconditions.checkNotNull( key, "User is not authenticated: " + getFullName() );
- }
-
- public abstract Iterable findSitesByName(String siteName);
-
- public abstract void addSite(Site site);
-
- public abstract void deleteSite(Site site);
-
- @Override
- public boolean equals(final Object obj) {
- return (this == obj) || ((obj instanceof User) && Objects.equals( getFullName(), ((User) obj).getFullName() ));
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode( getFullName() );
- }
-
- @Override
- public String toString() {
- return getFullName();
- }
-}
diff --git a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/platform/mac/AppleGUI.java b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/platform/mac/AppleGUI.java
index bd9746a7..9afb7924 100644
--- a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/platform/mac/AppleGUI.java
+++ b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/platform/mac/AppleGUI.java
@@ -21,7 +21,7 @@ package com.lyndir.masterpassword.gui.platform.mac;
import com.apple.eawt.*;
import com.lyndir.masterpassword.gui.GUI;
import com.lyndir.masterpassword.gui.view.PasswordFrame;
-import com.lyndir.masterpassword.gui.model.User;
+import com.lyndir.masterpassword.model.MPUser;
import javax.swing.*;
@@ -52,12 +52,4 @@ public class AppleGUI extends GUI {
}
} );
}
-
- @Override
- protected PasswordFrame newPasswordFrame(final User user) {
- PasswordFrame frame = super.newPasswordFrame( user );
- frame.setDefaultCloseOperation( WindowConstants.HIDE_ON_CLOSE );
-
- return frame;
- }
}
diff --git a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/AuthenticationPanel.java b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/AuthenticationPanel.java
index 882e2be0..c01d3181 100644
--- a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/AuthenticationPanel.java
+++ b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/AuthenticationPanel.java
@@ -20,9 +20,8 @@ package com.lyndir.masterpassword.gui.view;
import com.google.common.collect.ImmutableList;
import com.lyndir.masterpassword.gui.Res;
-import com.lyndir.masterpassword.gui.model.User;
+import com.lyndir.masterpassword.model.MPUser;
import com.lyndir.masterpassword.gui.util.Components;
-import com.lyndir.masterpassword.gui.view.UnlockFrame;
import java.awt.*;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -32,7 +31,7 @@ import javax.swing.*;
/**
* @author lhunath, 2014-06-11
*/
-public abstract class AuthenticationPanel extends Components.GradientPanel {
+public abstract class AuthenticationPanel> extends Components.GradientPanel {
protected final UnlockFrame unlockFrame;
protected final JLabel avatarLabel;
@@ -65,11 +64,12 @@ public abstract class AuthenticationPanel extends Components.GradientPanel {
}
@Nullable
- protected abstract User getSelectedUser();
+ protected abstract U getSelectedUser();
@Nonnull
public abstract char[] getMasterPassword();
+ @Nullable
public Component getFocusComponent() {
return null;
}
@@ -79,4 +79,6 @@ public abstract class AuthenticationPanel extends Components.GradientPanel {
}
public abstract void reset();
+
+ public abstract PasswordFrame, ?> newPasswordFrame();
}
diff --git a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/IncognitoAuthenticationPanel.java b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/IncognitoAuthenticationPanel.java
index 56f8c5e2..20eba05a 100644
--- a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/IncognitoAuthenticationPanel.java
+++ b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/IncognitoAuthenticationPanel.java
@@ -18,9 +18,12 @@
package com.lyndir.masterpassword.gui.view;
+import com.google.common.primitives.UnsignedInteger;
+import com.lyndir.masterpassword.MPMasterKey;
+import com.lyndir.masterpassword.MPResultType;
import com.lyndir.masterpassword.gui.Res;
+import com.lyndir.masterpassword.gui.model.IncognitoSite;
import com.lyndir.masterpassword.gui.model.IncognitoUser;
-import com.lyndir.masterpassword.gui.model.User;
import com.lyndir.masterpassword.gui.util.Components;
import java.awt.*;
import java.awt.event.ActionEvent;
@@ -34,7 +37,8 @@ import javax.swing.event.DocumentListener;
/**
* @author lhunath, 2014-06-11
*/
-public class IncognitoAuthenticationPanel extends AuthenticationPanel implements DocumentListener, ActionListener {
+@SuppressWarnings({ "serial", "MagicNumber" })
+public class IncognitoAuthenticationPanel extends AuthenticationPanel implements DocumentListener, ActionListener {
private final JTextField fullNameField;
private final JPasswordField masterPasswordField;
@@ -76,7 +80,19 @@ public class IncognitoAuthenticationPanel extends AuthenticationPanel implements
}
@Override
- protected User getSelectedUser() {
+ public PasswordFrame newPasswordFrame() {
+ return new PasswordFrame(getSelectedUser()) {
+ @Override
+ protected IncognitoSite createSite(final IncognitoUser user, final String siteName, final UnsignedInteger siteCounter,
+ final MPResultType resultType,
+ final MPMasterKey.Version algorithmVersion) {
+ return new IncognitoSite( siteName, siteCounter, resultType, algorithmVersion );
+ }
+ };
+ }
+
+ @Override
+ protected IncognitoUser getSelectedUser() {
return new IncognitoUser( fullNameField.getText() );
}
diff --git a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/ModelAuthenticationPanel.java b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/ModelAuthenticationPanel.java
index 711d9367..76f6315c 100644
--- a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/ModelAuthenticationPanel.java
+++ b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/ModelAuthenticationPanel.java
@@ -20,19 +20,17 @@ package com.lyndir.masterpassword.gui.view;
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
-import com.google.common.base.Function;
-import com.google.common.base.Preconditions;
import com.google.common.collect.*;
+import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.logging.Logger;
+import com.lyndir.masterpassword.MPMasterKey;
+import com.lyndir.masterpassword.MPResultType;
import com.lyndir.masterpassword.gui.Res;
-import com.lyndir.masterpassword.gui.model.ModelUser;
-import com.lyndir.masterpassword.model.MPUser;
-import com.lyndir.masterpassword.model.MPUserFileManager;
+import com.lyndir.masterpassword.model.*;
import com.lyndir.masterpassword.gui.util.Components;
import java.awt.*;
import java.awt.event.*;
import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
@@ -47,7 +45,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( ModelAuthenticationPanel.class );
- private final JComboBox userField;
+ private final JComboBox userField;
private final JLabel masterPasswordLabel;
private final JPasswordField masterPasswordField;
@@ -59,7 +57,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
avatarLabel.addMouseListener( new MouseAdapter() {
@Override
public void mouseClicked(final MouseEvent e) {
- ModelUser selectedUser = getSelectedUser();
+ MPFileUser selectedUser = getSelectedUser();
if (selectedUser != null) {
selectedUser.setAvatar( selectedUser.getAvatar() + 1 );
updateUser( false );
@@ -104,10 +102,10 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
@Override
protected void updateUser(boolean repack) {
- ModelUser selectedUser = getSelectedUser();
+ MPFileUser selectedUser = getSelectedUser();
if (selectedUser != null) {
avatarLabel.setIcon( Res.avatar( selectedUser.getAvatar() ) );
- boolean showPasswordField = !selectedUser.keySaved();
+ boolean showPasswordField = !selectedUser.isMasterKeyAvailable(); // TODO: is this the same as keySaved()?
if (masterPasswordField.isVisible() != showPasswordField) {
masterPasswordLabel.setVisible( showPasswordField );
masterPasswordField.setVisible( showPasswordField );
@@ -119,7 +117,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
}
@Override
- protected ModelUser getSelectedUser() {
+ protected MPFileUser getSelectedUser() {
int selectedIndex = userField.getSelectedIndex();
if (selectedIndex < 0)
return null;
@@ -143,7 +141,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
String fullName = JOptionPane.showInputDialog( ModelAuthenticationPanel.this, //
"Enter your full name, ensuring it is correctly spelled and capitalized:",
"New User", JOptionPane.QUESTION_MESSAGE );
- MPUserFileManager.get().addUser( new MPUser( fullName ) );
+ MPFileUserManager.get().addUser( new MPFileUser( fullName ) );
userField.setModel( new DefaultComboBoxModel<>( readConfigUsers() ) );
updateUser( true );
}
@@ -155,7 +153,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
addActionListener( new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
- ModelUser deleteUser = getSelectedUser();
+ MPFileUser deleteUser = getSelectedUser();
if (deleteUser == null)
return;
@@ -165,7 +163,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
"Delete User", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE ) == JOptionPane.CANCEL_OPTION)
return;
- MPUserFileManager.get().deleteUser( deleteUser.getModel() );
+ MPFileUserManager.get().deleteUser( deleteUser );
userField.setModel( new DefaultComboBoxModel<>( readConfigUsers() ) );
updateUser( true );
}
@@ -179,7 +177,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
public void actionPerformed(final ActionEvent e) {
JOptionPane.showMessageDialog( ModelAuthenticationPanel.this, //
strf( "Reads users and sites from the directory at:\n%s",
- MPUserFileManager.get().getPath().getAbsolutePath() ), //
+ MPFileUserManager.get().getPath().getAbsolutePath() ), //
"Help", JOptionPane.INFORMATION_MESSAGE );
}
} );
@@ -193,14 +191,19 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
masterPasswordField.setText( "" );
}
- private static ModelUser[] readConfigUsers() {
- return FluentIterable.from( MPUserFileManager.get().getUsers() ).transform( new Function() {
- @Nullable
+ @Override
+ public PasswordFrame newPasswordFrame() {
+ return new PasswordFrame(getSelectedUser()) {
@Override
- public ModelUser apply(@Nullable final MPUser model) {
- return new ModelUser( Preconditions.checkNotNull( model ) );
+ protected MPFileSite createSite(final MPFileUser user, final String siteName, final UnsignedInteger siteCounter, final MPResultType resultType,
+ final MPMasterKey.Version algorithmVersion) {
+ return new MPFileSite( user, siteName, siteCounter, resultType, algorithmVersion );
}
- } ).toArray( ModelUser.class );
+ };
+ }
+
+ private static MPFileUser[] readConfigUsers() {
+ return MPFileUserManager.get().getUsers().toArray( new MPFileUser[0] );
}
@Override
diff --git a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/PasswordFrame.java b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/PasswordFrame.java
index 2c68390e..41fdd5da 100755
--- a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/PasswordFrame.java
+++ b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/PasswordFrame.java
@@ -26,11 +26,12 @@ import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.common.primitives.UnsignedInteger;
import com.google.common.util.concurrent.*;
+import com.lyndir.lhunath.opal.system.util.ObjectUtils;
import com.lyndir.masterpassword.*;
import com.lyndir.masterpassword.gui.Res;
-import com.lyndir.masterpassword.gui.model.*;
import com.lyndir.masterpassword.gui.util.Components;
import com.lyndir.masterpassword.gui.util.UnsignedIntegerModel;
+import com.lyndir.masterpassword.model.*;
import java.awt.*;
import java.awt.datatransfer.StringSelection;
import java.awt.event.*;
@@ -44,28 +45,28 @@ import javax.swing.event.*;
/**
* @author lhunath, 2014-06-08
*/
-public class PasswordFrame extends JFrame implements DocumentListener {
+public abstract class PasswordFrame, S extends MPSite> extends JFrame implements DocumentListener {
@SuppressWarnings("FieldCanBeLocal")
- private final Components.GradientPanel root;
- private final JTextField siteNameField;
- private final JButton siteActionButton;
- private final JComboBox siteVersionField;
- private final JSpinner siteCounterField;
- private final UnsignedIntegerModel siteCounterModel;
- private final JComboBox resultTypeField;
- private final JPasswordField passwordField;
- private final JLabel tipLabel;
- private final JCheckBox maskPasswordField;
- private final char passwordEchoChar;
- private final Font passwordEchoFont;
- private final User user;
+ private final Components.GradientPanel root;
+ private final JTextField siteNameField;
+ private final JButton siteActionButton;
+ private final JComboBox siteVersionField;
+ private final JSpinner siteCounterField;
+ private final UnsignedIntegerModel siteCounterModel;
+ private final JComboBox resultTypeField;
+ private final JPasswordField passwordField;
+ private final JLabel tipLabel;
+ private final JCheckBox maskPasswordField;
+ private final char passwordEchoChar;
+ private final Font passwordEchoFont;
+ private final U user;
@Nullable
- private Site currentSite;
+ private S currentSite;
private boolean updatingUI;
- public PasswordFrame(final User user) {
+ public PasswordFrame(final U user) {
super( "Master Password" );
this.user = user;
@@ -122,7 +123,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
public void actionPerformed(final ActionEvent e) {
if (currentSite == null)
return;
- if (currentSite instanceof ModelSite)
+ if (currentSite instanceof MPFileSite)
PasswordFrame.this.user.deleteSite( currentSite );
else
PasswordFrame.this.user.addSite( currentSite );
@@ -140,7 +141,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
JComponent siteSettings = Components.boxLayout( BoxLayout.LINE_AXIS, //
resultTypeField = Components.comboBox( types ), //
Components.stud(), //
- siteVersionField = Components.comboBox( MasterKey.Version.values() ), //
+ siteVersionField = Components.comboBox( MPMasterKey.Version.values() ), //
Components.stud(), //
siteCounterField = Components.spinner( siteCounterModel ) );
sitePanel.add( siteSettings );
@@ -155,7 +156,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
siteVersionField.setFont( Res.valueFont().deriveFont( 12f ) );
siteVersionField.setAlignmentX( RIGHT_ALIGNMENT );
- siteVersionField.setSelectedItem( MasterKey.Version.CURRENT );
+ siteVersionField.setSelectedItem( MPMasterKey.Version.CURRENT );
siteVersionField.addItemListener( new ItemListener() {
@Override
public void itemStateChanged(final ItemEvent e) {
@@ -226,27 +227,27 @@ public class PasswordFrame extends JFrame implements DocumentListener {
final String siteNameQuery = siteNameField.getText();
if (updatingUI)
return Futures.immediateCancelledFuture();
- if ((siteNameQuery == null) || siteNameQuery.isEmpty() || !user.isKeyAvailable()) {
+ if ((siteNameQuery == null) || siteNameQuery.isEmpty() || !user.isMasterKeyAvailable()) {
siteActionButton.setVisible( false );
tipLabel.setText( null );
passwordField.setText( null );
return Futures.immediateCancelledFuture();
}
- MPResultType resultType = resultTypeField.getModel().getElementAt( resultTypeField.getSelectedIndex() );
- MasterKey.Version siteVersion = siteVersionField.getItemAt( siteVersionField.getSelectedIndex() );
- UnsignedInteger siteCounter = siteCounterModel.getNumber();
+ MPResultType resultType = resultTypeField.getModel().getElementAt( resultTypeField.getSelectedIndex() );
+ MPMasterKey.Version siteVersion = siteVersionField.getItemAt( siteVersionField.getSelectedIndex() );
+ UnsignedInteger siteCounter = siteCounterModel.getNumber();
- Iterable siteResults = user.findSitesByName( siteNameQuery );
+ Iterable siteResults = user.findSites( siteNameQuery );
if (!allowNameCompletion)
- siteResults = FluentIterable.from( siteResults ).filter( new Predicate() {
+ siteResults = FluentIterable.from( siteResults ).filter( new Predicate() {
@Override
- public boolean apply(@Nullable final Site siteResult) {
+ public boolean apply(@Nullable final S siteResult) {
return (siteResult != null) && siteNameQuery.equals( siteResult.getSiteName() );
}
} );
- final Site site = ifNotNullElse( Iterables.getFirst( siteResults, null ),
- new IncognitoSite( siteNameQuery, siteCounter, resultType, siteVersion ) );
+ final S site = ifNotNullElse( Iterables.getFirst( siteResults, null ),
+ createSite( user, siteNameQuery, siteCounter, resultType, siteVersion ) );
if ((currentSite != null) && currentSite.getSiteName().equals( site.getSiteName() )) {
site.setResultType( resultType );
site.setAlgorithmVersion( siteVersion );
@@ -257,8 +258,9 @@ public class PasswordFrame extends JFrame implements DocumentListener {
@Override
public String call()
throws Exception {
- return user.getKey()
- .siteResult( site.getSiteName(), site.getSiteCounter(), MPKeyPurpose.Authentication, null, site.getResultType(), null, site.getAlgorithmVersion() );
+ return user.getMasterKey()
+ .siteResult( site.getSiteName(), site.getSiteCounter(), MPKeyPurpose.Authentication, null, site.getResultType(),
+ null, site.getAlgorithmVersion() );
}
} );
Futures.addCallback( passwordFuture, new FutureCallback() {
@@ -269,8 +271,8 @@ public class PasswordFrame extends JFrame implements DocumentListener {
public void run() {
updatingUI = true;
currentSite = site;
- siteActionButton.setVisible( user instanceof ModelUser );
- if (currentSite instanceof ModelSite)
+ siteActionButton.setVisible( user instanceof MPFileUser );
+ if (currentSite instanceof MPFileSite)
siteActionButton.setText( "Delete Site" );
else
siteActionButton.setText( "Add Site" );
@@ -296,6 +298,9 @@ public class PasswordFrame extends JFrame implements DocumentListener {
return passwordFuture;
}
+ protected abstract S createSite(U user, String siteName, UnsignedInteger siteCounter, MPResultType resultType,
+ MPMasterKey.Version algorithmVersion);
+
@Override
public void insertUpdate(final DocumentEvent e) {
updatePassword( true );
diff --git a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/UnlockFrame.java b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/UnlockFrame.java
index cd059b97..ec354b29 100644
--- a/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/UnlockFrame.java
+++ b/platform-independent/gui-java/src/main/java/com/lyndir/masterpassword/gui/view/UnlockFrame.java
@@ -22,9 +22,9 @@ import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
import com.lyndir.masterpassword.MPIdenticon;
import com.lyndir.masterpassword.gui.*;
-import com.lyndir.masterpassword.gui.model.User;
+import com.lyndir.masterpassword.model.MPUser;
import com.lyndir.masterpassword.gui.util.Components;
-import com.lyndir.masterpassword.model.IncorrectMasterPasswordException;
+import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException;
import java.awt.*;
import java.awt.event.*;
import java.util.concurrent.Future;
@@ -36,6 +36,7 @@ import javax.swing.*;
/**
* @author lhunath, 2014-06-08
*/
+@SuppressWarnings({ "MagicNumber", "serial" })
public class UnlockFrame extends JFrame {
private final SignInCallback signInCallback;
@@ -43,10 +44,10 @@ public class UnlockFrame extends JFrame {
private final JLabel identiconLabel;
private final JButton signInButton;
private final JPanel authenticationContainer;
- private AuthenticationPanel authenticationPanel;
+ private AuthenticationPanel> authenticationPanel;
private Future> identiconFuture;
private boolean incognito;
- private User user;
+ private MPUser> user;
public UnlockFrame(final SignInCallback signInCallback) {
super( "Unlock Master Password" );
@@ -155,7 +156,7 @@ public class UnlockFrame extends JFrame {
} );
}
- void updateUser(@Nullable final User user) {
+ void updateUser(@Nullable final MPUser> user) {
this.user = user;
checkSignIn();
}
@@ -213,12 +214,12 @@ public class UnlockFrame extends JFrame {
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
- signInCallback.signedIn( user );
+ signInCallback.signedIn( authenticationPanel.newPasswordFrame() );
dispose();
}
} );
}
- catch (final IncorrectMasterPasswordException e) {
+ catch (final MPIncorrectMasterPasswordException e) {
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
@@ -237,6 +238,6 @@ public class UnlockFrame extends JFrame {
public interface SignInCallback {
- void signedIn(User user);
+ void signedIn(PasswordFrame, ?> passwordFrame);
}
}