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