From 8d7c351912ca1243a858771a8c66ad77c4999657 Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Mon, 14 May 2018 12:24:15 -0400 Subject: [PATCH] Initial implementation of questions support. --- .../lyndir/masterpassword/MPAlgorithm.java | 5 ++ .../lyndir/masterpassword/MPAlgorithmV0.java | 5 ++ .../masterpassword/model/MPFileQuestion.java | 84 +++++++++++++++++++ .../masterpassword/model/MPFileSite.java | 30 ++++--- .../masterpassword/model/MPFileUser.java | 4 +- .../masterpassword/model/MPQuestion.java | 41 +++++++++ .../lyndir/masterpassword/model/MPSite.java | 31 +++++-- .../gui/model/IncognitoSite.java | 11 ++- .../gui/view/PasswordFrame.java | 4 +- 9 files changed, 191 insertions(+), 24 deletions(-) create mode 100644 core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileQuestion.java create mode 100644 core/java/model/src/main/java/com/lyndir/masterpassword/model/MPQuestion.java diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithm.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithm.java index add7732a..00496abe 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithm.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithm.java @@ -66,6 +66,11 @@ public abstract class MPAlgorithm { */ public abstract MPResultType mpw_default_login_type(); + /** + * mpw: defaults: answer result type. + */ + public abstract MPResultType mpw_default_answer_type(); + /** * mpw: defaults: initial counter value. */ diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV0.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV0.java index 1ae201d7..ad4706bd 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV0.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPAlgorithmV0.java @@ -256,6 +256,11 @@ public class MPAlgorithmV0 extends MPAlgorithm { return MPResultType.GeneratedName; } + @Override + public MPResultType mpw_default_answer_type() { + return MPResultType.GeneratedPhrase; + } + @Override public UnsignedInteger mpw_default_counter() { return UnsignedInteger.ONE; diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileQuestion.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileQuestion.java new file mode 100644 index 00000000..9aff3cb8 --- /dev/null +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileQuestion.java @@ -0,0 +1,84 @@ +//============================================================================== +// 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 static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse; + +import com.lyndir.masterpassword.*; +import javax.annotation.Nullable; + + +/** + * @author lhunath, 2018-05-14 + */ +public class MPFileQuestion extends MPQuestion { + + private final MPSite site; + + private String keyword; + @Nullable + private String state; + private MPResultType type; + + public MPFileQuestion(final MPSite site, final String keyword, @Nullable final String state, @Nullable final MPResultType type) { + this.site = site; + this.keyword = keyword; + this.state = state; + this.type = ifNotNullElse( type, site.getAlgorithm().mpw_default_answer_type() ); + } + + @Override + public MPSite getSite() { + return site; + } + + @Override + public String getKeyword() { + return keyword; + } + + public void setKeyword(final String keyword) { + this.keyword = keyword; + } + + public void setAnswer(final MPResultType type, @Nullable final String answer) + throws MPKeyUnavailableException { + this.type = type; + + if (answer == null) + this.state = null; + else + this.state = getSite().getState( + MPKeyPurpose.Recovery, getKeyword(), null, type, answer ); + } + + @Override + public MPResultType getType() { + return type; + } + + public void setType(final MPResultType type) { + this.type = type; + } + + public String getAnswer() + throws MPKeyUnavailableException { + return getAnswer( state ); + } +} 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 index 7b87881a..db6e8233 100644 --- 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 @@ -22,6 +22,7 @@ import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*; import com.google.common.primitives.UnsignedInteger; import com.lyndir.masterpassword.*; +import java.util.*; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.joda.time.Instant; @@ -36,9 +37,9 @@ public class MPFileSite extends MPSite { private final MPFileUser user; private String siteName; + private UnsignedInteger siteCounter; @Nullable private String siteState; - private UnsignedInteger siteCounter; private MPResultType resultType; private MPAlgorithm algorithm; @@ -51,6 +52,8 @@ public class MPFileSite extends MPSite { private int uses; private ReadableInstant lastUsed; + private final Collection questions = new LinkedHashSet<>(); + public MPFileSite(final MPFileUser user, final String siteName) { this( user, siteName, null, null, user.getAlgorithm() ); } @@ -66,11 +69,12 @@ public class MPFileSite extends MPSite { @Nullable final String loginState, @Nullable final MPResultType loginType, @Nullable final String url, final int uses, final ReadableInstant lastUsed) { this.user = user; + this.algorithm = algorithm; + this.siteName = siteName; this.siteState = siteState; - this.siteCounter = ifNotNullElse( siteCounter, user.getAlgorithm().mpw_default_counter() ); - this.resultType = ifNotNullElse( resultType, user.getAlgorithm().mpw_default_password_type() ); - this.algorithm = algorithm; + this.siteCounter = ifNotNullElse( siteCounter, getAlgorithm().mpw_default_counter() ); + this.resultType = ifNotNullElse( resultType, getAlgorithm().mpw_default_password_type() ); this.loginState = loginState; this.loginType = ifNotNullElse( loginType, getAlgorithm().mpw_default_login_type() ); this.url = url; @@ -116,15 +120,15 @@ public class MPFileSite extends MPSite { return siteState; } - public void setSitePassword(final MPResultType resultType, @Nullable final String result) + public void setSitePassword(final MPResultType resultType, @Nullable final String password) throws MPKeyUnavailableException { this.resultType = resultType; - if (result == null) + if (password == null) this.siteState = null; else - this.siteState = user.getMasterKey().siteState( - siteName, siteCounter, MPKeyPurpose.Authentication, null, resultType, result, algorithm ); + this.siteState = getState( + MPKeyPurpose.Authentication, null, siteCounter, resultType, password ); } @Override @@ -155,7 +159,6 @@ public class MPFileSite extends MPSite { @Override public void setLoginType(@Nullable final MPResultType loginType) { this.loginType = ifNotNullElse( loginType, getAlgorithm().mpw_default_login_type() ); - } @Override @@ -168,6 +171,11 @@ public class MPFileSite extends MPSite { this.algorithm = algorithm; } + @Override + public Collection getQuestions() { + return Collections.unmodifiableCollection( questions ); + } + @Nullable public String getLoginState() { return loginState; @@ -176,9 +184,7 @@ public class MPFileSite extends MPSite { public void setLoginName(@Nonnull final MPResultType loginType, @Nonnull final String loginName) throws MPKeyUnavailableException { this.loginType = loginType; - this.loginState = user.getMasterKey().siteState( - siteName, algorithm.mpw_default_counter(), MPKeyPurpose.Identification, null, - this.loginType, loginName, algorithm ); + this.loginState = getState( MPKeyPurpose.Identification, null, null, this.loginType, loginName ); } @Nullable 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 index 97fbbb48..6aaddfbd 100755 --- 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 @@ -39,7 +39,7 @@ public class MPFileUser extends MPUser implements Comparable sites = Sets.newHashSet(); + private final Collection sites = new LinkedHashSet<>(); @Nullable private byte[] keyID; @@ -136,7 +136,7 @@ public class MPFileUser extends MPUser implements Comparable getSites() { + public Collection getSites() { return Collections.unmodifiableCollection( sites ); } diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPQuestion.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPQuestion.java new file mode 100644 index 00000000..13540e8b --- /dev/null +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPQuestion.java @@ -0,0 +1,41 @@ +//============================================================================== +// 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.lyndir.masterpassword.*; +import javax.annotation.Nullable; + + +/** + * @author lhunath, 2018-05-14 + */ +public abstract class MPQuestion { + + public abstract MPSite getSite(); + + public abstract String getKeyword(); + + public abstract MPResultType getType(); + + public String getAnswer(@Nullable final String state) + throws MPKeyUnavailableException { + + return getSite().getResult( MPKeyPurpose.Recovery, getKeyword(), null, getType(), state ); + } +} 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 0d560d76..9da33720 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 @@ -18,10 +18,12 @@ package com.lyndir.masterpassword.model; +import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*; import static com.lyndir.lhunath.opal.system.util.StringUtils.*; import com.google.common.primitives.UnsignedInteger; import com.lyndir.masterpassword.*; +import java.util.Collection; import java.util.Objects; import javax.annotation.Nullable; @@ -54,21 +56,40 @@ public abstract class MPSite { public abstract void setAlgorithm(MPAlgorithm algorithm); public String getResult(final MPKeyPurpose keyPurpose, @Nullable final String keyContext, - @Nullable final String siteContent) + @Nullable final String state) + throws MPKeyUnavailableException { + + return getResult( keyPurpose, keyContext, getSiteCounter(), getResultType(), state ); + } + + protected String getResult(final MPKeyPurpose keyPurpose, @Nullable final String keyContext, + @Nullable final UnsignedInteger siteCounter, final MPResultType type, + @Nullable final String state) throws MPKeyUnavailableException { return getUser().getMasterKey().siteResult( - getSiteName(), getSiteCounter(), keyPurpose, keyContext, getResultType(), siteContent, getAlgorithm() ); + getSiteName(), ifNotNullElse( siteCounter, getAlgorithm().mpw_default_counter() ), keyPurpose, keyContext, + type, state, getAlgorithm() ); + } + + protected String getState(final MPKeyPurpose keyPurpose, @Nullable final String keyContext, + @Nullable final UnsignedInteger siteCounter, final MPResultType type, + @Nullable final String state) + throws MPKeyUnavailableException { + + return getUser().getMasterKey().siteState( + getSiteName(), ifNotNullElse( siteCounter, getAlgorithm().mpw_default_counter() ), keyPurpose, keyContext, + type, state, getAlgorithm() ); } public String getLogin(@Nullable final String loginContent) throws MPKeyUnavailableException { - return getUser().getMasterKey().siteResult( - getSiteName(), getAlgorithm().mpw_default_counter(), MPKeyPurpose.Identification, null, - getLoginType(), loginContent, getAlgorithm() ); + return getResult( MPKeyPurpose.Identification, null,null, getLoginType(), loginContent ); } + public abstract Collection getQuestions(); + @Override public boolean equals(final Object obj) { return (this == obj) || ((obj instanceof MPSite) && Objects.equals( getSiteName(), ((MPSite) obj).getSiteName() )); 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 98683a8b..0f381cee 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,11 +20,13 @@ package com.lyndir.masterpassword.gui.model; import static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse; +import com.google.common.collect.ImmutableCollection; +import com.google.common.collect.ImmutableList; import com.google.common.primitives.UnsignedInteger; import com.lyndir.masterpassword.MPAlgorithm; import com.lyndir.masterpassword.MPResultType; -import com.lyndir.masterpassword.model.MPSite; -import com.lyndir.masterpassword.model.MPUser; +import com.lyndir.masterpassword.model.*; +import java.util.Collection; import javax.annotation.Nullable; @@ -95,6 +97,11 @@ public class IncognitoSite extends MPSite { this.algorithm = algorithm; } + @Override + public Collection getQuestions() { + return ImmutableList.of(); + } + @Override public UnsignedInteger getSiteCounter() { return siteCounter; 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 bd6c39dd..8ce8d883 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 @@ -260,9 +260,7 @@ public abstract class PasswordFrame, S extends MPSite> exten @Override public String call() throws Exception { - return user.getMasterKey() - .siteResult( site.getSiteName(), site.getSiteCounter(), MPKeyPurpose.Authentication, null, site.getResultType(), - null, site.getAlgorithm() ); + return site.getResult( MPKeyPurpose.Authentication, null, null ); } } ); Futures.addCallback( passwordFuture, new FutureCallback() {