2
0

Java relocation.

This commit is contained in:
Maarten Billemont
2018-06-05 20:19:10 -04:00
parent aef8422102
commit 190a241a25
57 changed files with 5 additions and 5 deletions

View File

@@ -0,0 +1,24 @@
plugins {
id 'java-library'
id 'net.ltgt.apt' version '0.9'
}
description = 'Master Password Site Model'
dependencies {
implementation group: 'com.lyndir.lhunath.opal', name: 'opal-system', version: '1.7-p1'
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.9.5'
api project( ':masterpassword-algorithm' )
api group: 'joda-time', name: 'joda-time', version: '2.4'
api group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.9.5'
api group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.5'
api group: 'com.google.code.findbugs', name: 'findbugs-annotations', version: '3.0.1'
compileOnly group: 'com.google.auto.value', name: 'auto-value', version: '1.2'
apt group: 'com.google.auto.value', name: 'auto-value', version: '1.2'
testCompile group: 'org.testng', name: 'testng', version: '6.8.5'
testCompile group: 'ch.qos.logback', name: 'logback-classic', version: '1.1.2'
}
test.useTestNG()

View File

@@ -0,0 +1,46 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
/**
* @author lhunath, 2016-10-29
*/
public final class MPConstant {
/* Environment */
/**
* mpw: default path to look for run configuration files if the platform default is not desired.
*/
public static final String env_rcDir = "MPW_RCDIR";
/**
* mpw: permit automatic update checks.
*/
public static final String env_checkUpdates = "MPW_CHECKUPDATES";
/* Algorithm */
public static final int MS_PER_S = 1000;
public static final DateTimeFormatter dateTimeFormatter = ISODateTimeFormat.dateTimeNoMillis().withZoneUTC();
}

View File

@@ -0,0 +1,40 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model;
import com.lyndir.masterpassword.MPException;
/**
* @author lhunath, 14-12-17
*/
public class MPIncorrectMasterPasswordException extends MPException {
private final MPUser<?> user;
public MPIncorrectMasterPasswordException(final MPUser<?> user) {
super( "Incorrect master password for user: " + user.getFullName() );
this.user = user;
}
public MPUser<?> getUser() {
return user;
}
}

View File

@@ -0,0 +1,51 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model;
import com.lyndir.masterpassword.*;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* @author lhunath, 2018-05-15
*/
public interface MPQuestion extends Comparable<MPQuestion> {
// -- Meta
@Nonnull
String getKeyword();
// -- Algorithm
@Nonnull
MPResultType getType();
void setType(MPResultType type);
@Nonnull
String getAnswer(@Nullable String state)
throws MPKeyUnavailableException, MPAlgorithmException;
// -- Relationship
@Nonnull
MPSite<?> getSite();
}

View File

@@ -0,0 +1,71 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model;
import com.google.common.primitives.UnsignedInteger;
import com.lyndir.masterpassword.*;
import java.util.Collection;
import javax.annotation.Nullable;
/**
* @author lhunath, 2018-05-14
*/
public interface MPSite<Q extends MPQuestion> extends Comparable<MPSite<?>> {
// - Meta
String getName();
void setName(String name);
// - Algorithm
MPAlgorithm getAlgorithm();
void setAlgorithm(MPAlgorithm algorithm);
UnsignedInteger getCounter();
void setCounter(UnsignedInteger counter);
MPResultType getResultType();
void setResultType(MPResultType resultType);
MPResultType getLoginType();
void setLoginType(@Nullable MPResultType loginType);
String getResult(MPKeyPurpose keyPurpose, @Nullable String keyContext, @Nullable String state)
throws MPKeyUnavailableException, MPAlgorithmException;
String getLogin(@Nullable String state)
throws MPKeyUnavailableException, MPAlgorithmException;
// - Relations
MPUser<? extends MPSite<?>> getUser();
void addQuestion(Q question);
void deleteQuestion(Q question);
Collection<Q> getQuestions();
}

View File

@@ -0,0 +1,61 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model;
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
import java.util.Objects;
import org.jetbrains.annotations.NotNull;
/**
* @author lhunath, 14-12-07
*/
public class MPSiteResult implements Comparable<MPSiteResult> {
private final MPSite<?> site;
public MPSiteResult(final MPSite<?> site) {
this.site = site;
}
public MPSite<?> getSite() {
return site;
}
@Override
public int hashCode() {
return Objects.hashCode( getSite() );
}
@Override
public boolean equals(final Object obj) {
return (this == obj) || ((obj instanceof MPSiteResult) && Objects.equals( getSite(), ((MPSiteResult) obj).getSite() ));
}
@Override
public int compareTo(@NotNull final MPSiteResult o) {
return getSite().compareTo( o.getSite() );
}
@Override
public String toString() {
return strf( "{%s: %s}", getClass().getSimpleName(), getSite() );
}
}

View File

@@ -0,0 +1,96 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model;
import com.lyndir.masterpassword.*;
import java.util.Collection;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* @author lhunath, 2018-05-14
*/
public interface MPUser<S extends MPSite<?>> extends Comparable<MPUser<?>> {
// - Meta
int getAvatar();
void setAvatar(int avatar);
@Nonnull
String getFullName();
// - Algorithm
@Nonnull
MPAlgorithm getAlgorithm();
void setAlgorithm(MPAlgorithm algorithm);
@Nullable
byte[] getKeyID();
@Nullable
String exportKeyID();
/**
* Performs an authentication attempt against the keyID for this user.
*
* Note: If a keyID is not set, authentication will always succeed and the keyID will be set to match the given master password.
*
* @param masterPassword The password to authenticate with.
* You cannot re-use this array after passing it in, authentication will destroy its contents.
*
* @throws MPIncorrectMasterPasswordException If authentication fails due to the given master password not matching the user's keyID.
*/
void authenticate(char[] masterPassword)
throws MPIncorrectMasterPasswordException, MPAlgorithmException;
/**
* Performs an authentication attempt against the keyID for this user.
*
* Note: If a keyID is not set, authentication will always succeed and the keyID will be set to match the given key.
*
* @param masterKey The master key to authenticate with.
*
* @throws MPIncorrectMasterPasswordException If authentication fails due to the given master password not matching the user's keyID.
*/
void authenticate(MPMasterKey masterKey)
throws MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException;
boolean isMasterKeyAvailable();
@Nonnull
MPMasterKey getMasterKey()
throws MPKeyUnavailableException;
// - Relations
void addSite(S site);
void deleteSite(S site);
@Nonnull
Collection<S> getSites();
@Nonnull
Collection<S> findSites(String query);
}

View File

@@ -0,0 +1,54 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import java.util.Collection;
import java.util.Map;
/**
* @author lhunath, 14-12-05
*/
public abstract class MPUserManager<U extends MPUser<?>> {
private final Map<String, U> usersByName = Maps.newHashMap();
protected MPUserManager(final Iterable<U> users) {
for (final U user : users)
usersByName.put( user.getFullName(), user );
}
public Collection<U> getUsers() {
return ImmutableList.copyOf( usersByName.values() );
}
public U getUserNamed(final String fullName) {
return usersByName.get( fullName );
}
public void addUser(final U user) {
usersByName.put( user.getFullName(), user );
}
public void deleteUser(final U user) {
usersByName.remove( user.getFullName() );
}
}

View File

@@ -0,0 +1,92 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model.impl;
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
import com.lyndir.masterpassword.*;
import com.lyndir.masterpassword.model.MPQuestion;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
/**
* @author lhunath, 2018-05-14
*/
public abstract class MPBasicQuestion implements MPQuestion {
private final String keyword;
private MPResultType type;
protected MPBasicQuestion(final String keyword, final MPResultType type) {
this.keyword = keyword;
this.type = type;
}
@Nonnull
@Override
public String getKeyword() {
return keyword;
}
@Nonnull
@Override
public MPResultType getType() {
return type;
}
@Override
public void setType(final MPResultType type) {
this.type = type;
}
@Nonnull
@Override
public String getAnswer(@Nullable final String state)
throws MPKeyUnavailableException, MPAlgorithmException {
return getSite().getResult( MPKeyPurpose.Recovery, getKeyword(), null, getType(), state );
}
@Nonnull
@Override
public abstract MPBasicSite<?> getSite();
@Override
public int hashCode() {
return Objects.hashCode( getKeyword() );
}
@Override
public boolean equals(final Object obj) {
return (this == obj) || ((obj instanceof MPQuestion) && Objects.equals( getKeyword(), ((MPQuestion) obj).getKeyword() ));
}
@Override
public int compareTo(@NotNull final MPQuestion o) {
return getKeyword().compareTo( o.getKeyword() );
}
@Override
public String toString() {
return strf( "{%s: %s}", getClass().getSimpleName(), getKeyword() );
}
}

View File

@@ -0,0 +1,175 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model.impl;
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 com.lyndir.masterpassword.model.MPQuestion;
import com.lyndir.masterpassword.model.MPSite;
import java.util.*;
import javax.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
/**
* @author lhunath, 14-12-16
*/
public abstract class MPBasicSite<Q extends MPQuestion> implements MPSite<Q> {
private String name;
private MPAlgorithm algorithm;
private UnsignedInteger counter;
private MPResultType resultType;
private MPResultType loginType;
private final Collection<Q> questions = new LinkedHashSet<>();
protected MPBasicSite(final String name, final MPAlgorithm algorithm) {
this( name, algorithm, null, null, null );
}
protected MPBasicSite(final String name, final MPAlgorithm algorithm, @Nullable final UnsignedInteger counter,
@Nullable final MPResultType resultType, @Nullable final MPResultType loginType) {
this.name = name;
this.algorithm = algorithm;
this.counter = (counter == null)? algorithm.mpw_default_counter(): counter;
this.resultType = (resultType == null)? algorithm.mpw_default_result_type(): resultType;
this.loginType = (loginType == null)? algorithm.mpw_default_login_type(): loginType;
}
@Override
public String getName() {
return name;
}
@Override
public void setName(final String name) {
this.name = name;
}
@Override
public MPAlgorithm getAlgorithm() {
return algorithm;
}
@Override
public void setAlgorithm(final MPAlgorithm algorithm) {
this.algorithm = algorithm;
}
@Override
public UnsignedInteger getCounter() {
return counter;
}
@Override
public void setCounter(final UnsignedInteger counter) {
this.counter = counter;
}
@Override
public MPResultType getResultType() {
return resultType;
}
@Override
public void setResultType(final MPResultType resultType) {
this.resultType = resultType;
}
@Override
public MPResultType getLoginType() {
return loginType;
}
@Override
public void setLoginType(@Nullable final MPResultType loginType) {
this.loginType = ifNotNullElse( loginType, getAlgorithm().mpw_default_login_type() );
}
@Override
public String getResult(final MPKeyPurpose keyPurpose, @Nullable final String keyContext, @Nullable final String state)
throws MPKeyUnavailableException, MPAlgorithmException {
return getResult( keyPurpose, keyContext, getCounter(), getResultType(), state );
}
protected String getResult(final MPKeyPurpose keyPurpose, @Nullable final String keyContext,
@Nullable final UnsignedInteger counter, final MPResultType type, @Nullable final String state)
throws MPKeyUnavailableException, MPAlgorithmException {
return getUser().getMasterKey().siteResult(
getName(), getAlgorithm(), ifNotNullElse( counter, getAlgorithm().mpw_default_counter() ),
keyPurpose, keyContext, type, state );
}
protected String getState(final MPKeyPurpose keyPurpose, @Nullable final String keyContext,
@Nullable final UnsignedInteger counter, final MPResultType type, final String state)
throws MPKeyUnavailableException, MPAlgorithmException {
return getUser().getMasterKey().siteState(
getName(), getAlgorithm(), ifNotNullElse( counter, getAlgorithm().mpw_default_counter() ),
keyPurpose, keyContext, type, state );
}
@Override
public String getLogin(@Nullable final String state)
throws MPKeyUnavailableException, MPAlgorithmException {
return getResult( MPKeyPurpose.Identification, null, null, getLoginType(), state );
}
@Override
public void addQuestion(final Q question) {
questions.add( question );
}
@Override
public void deleteQuestion(final Q question) {
questions.remove( question );
}
@Override
public Collection<Q> getQuestions() {
return Collections.unmodifiableCollection( questions );
}
@Override
public int hashCode() {
return Objects.hashCode( getName() );
}
@Override
public boolean equals(final Object obj) {
return (this == obj) || ((obj instanceof MPSite) && Objects.equals( getName(), ((MPSite<?>) obj).getName() ));
}
@Override
public int compareTo(@NotNull final MPSite<?> o) {
return getName().compareTo( o.getName() );
}
@Override
public String toString() {
return strf( "{%s: %s}", getClass().getSimpleName(), getName() );
}
}

View File

@@ -0,0 +1,190 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model.impl;
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
import com.google.common.collect.ImmutableList;
import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.*;
import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException;
import com.lyndir.masterpassword.model.MPUser;
import java.util.*;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* @author lhunath, 2014-06-08
*/
public abstract class MPBasicUser<S extends MPBasicSite<?>> implements MPUser<S> {
protected final Logger logger = Logger.get( getClass() );
private int avatar;
private final String fullName;
private MPAlgorithm algorithm;
@Nullable
protected MPMasterKey masterKey;
private final Collection<S> sites = new LinkedHashSet<>();
protected MPBasicUser(final String fullName, final MPAlgorithm algorithm) {
this( 0, fullName, algorithm );
}
protected MPBasicUser(final int avatar, final String fullName, final MPAlgorithm algorithm) {
this.avatar = avatar;
this.fullName = fullName;
this.algorithm = algorithm;
}
@Override
public int getAvatar() {
return avatar;
}
@Override
public void setAvatar(final int avatar) {
this.avatar = avatar;
}
@Nonnull
@Override
public String getFullName() {
return fullName;
}
@Nonnull
@Override
public MPAlgorithm getAlgorithm() {
return algorithm;
}
@Override
public void setAlgorithm(final MPAlgorithm algorithm) {
this.algorithm = algorithm;
}
@Nullable
@Override
public byte[] getKeyID() {
try {
return getMasterKey().getKeyID( getAlgorithm() );
}
catch (final MPException e) {
logger.wrn( e, "While deriving key ID for user: %s", this );
return null;
}
}
@Nullable
@Override
public String exportKeyID() {
return CodeUtils.encodeHex( getKeyID() );
}
@Override
public void authenticate(final char[] masterPassword)
throws MPIncorrectMasterPasswordException, MPAlgorithmException {
try {
authenticate( new MPMasterKey( getFullName(), masterPassword ) );
}
catch (final MPKeyUnavailableException e) {
throw new IllegalStateException( e );
}
}
@Override
public void authenticate(final MPMasterKey masterKey)
throws MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException {
if (!masterKey.getFullName().equals( getFullName() ))
throw new IllegalArgumentException(
"Master key (for " + masterKey.getFullName() + ") is not for this user (" + getFullName() + ")." );
byte[] keyID = getKeyID();
if ((keyID != null) && !Arrays.equals( masterKey.getKeyID( getAlgorithm() ), keyID ))
throw new MPIncorrectMasterPasswordException( this );
this.masterKey = masterKey;
}
@Override
public boolean isMasterKeyAvailable() {
return masterKey != null;
}
@Nonnull
@Override
public MPMasterKey getMasterKey()
throws MPKeyUnavailableException {
if (masterKey == null)
throw new MPKeyUnavailableException( "Master key was not yet set." );
return masterKey;
}
@Override
public void addSite(final S site) {
sites.add( site );
}
@Override
public void deleteSite(final S site) {
sites.remove( site );
}
@Nonnull
@Override
public Collection<S> getSites() {
return Collections.unmodifiableCollection( sites );
}
@Nonnull
@Override
public Collection<S> findSites(final String query) {
ImmutableList.Builder<S> results = ImmutableList.builder();
for (final S site : getSites())
if (site.getName().startsWith( query ))
results.add( site );
return results.build();
}
@Override
public int hashCode() {
return Objects.hashCode( getFullName() );
}
@Override
public boolean equals(final Object obj) {
return (this == obj) || ((obj instanceof MPUser) && Objects.equals( getFullName(), ((MPUser<?>) obj).getFullName() ));
}
@Override
public int compareTo(final MPUser<?> o) {
return getFullName().compareTo( o.getFullName() );
}
@Override
public String toString() {
return strf( "{%s: %s}", getClass().getSimpleName(), getFullName() );
}
}

View File

@@ -0,0 +1,67 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model.impl;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
import com.lyndir.masterpassword.*;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* @author lhunath, 2018-05-14
*/
public class MPFileQuestion extends MPBasicQuestion {
private final MPFileSite site;
@Nullable
private String state;
public MPFileQuestion(final MPFileSite site, final String keyword,
@Nullable final MPResultType type, @Nullable final String state) {
super( keyword, ifNotNullElse( type, site.getAlgorithm().mpw_default_answer_type() ) );
this.site = site;
this.state = state;
}
public String getAnswer()
throws MPKeyUnavailableException, MPAlgorithmException {
return getAnswer( state );
}
public void setAnswer(final MPResultType type, @Nullable final String answer)
throws MPKeyUnavailableException, MPAlgorithmException {
setType( type );
if (answer == null)
this.state = null;
else
this.state = getSite().getState(
MPKeyPurpose.Recovery, getKeyword(), null, getType(), answer );
}
@Nonnull
@Override
public MPFileSite getSite() {
return site;
}
}

View File

@@ -0,0 +1,150 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model.impl;
import com.google.common.primitives.UnsignedInteger;
import com.lyndir.masterpassword.*;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.joda.time.Instant;
import org.joda.time.ReadableInstant;
/**
* @author lhunath, 14-12-05
*/
public class MPFileSite extends MPBasicSite<MPFileQuestion> {
private final MPFileUser user;
@Nullable
private String url;
private int uses;
private ReadableInstant lastUsed;
@Nullable
private String resultState;
@Nullable
private String loginState;
public MPFileSite(final MPFileUser user, final String name) {
this( user, name, null, null, null );
}
public MPFileSite(final MPFileUser user, final String name,
@Nullable final MPAlgorithm algorithm, @Nullable final UnsignedInteger counter,
@Nullable final MPResultType resultType) {
this( user, name, algorithm, counter, resultType, null,
null, null, null, 0, new Instant() );
}
protected MPFileSite(final MPFileUser user, final String name,
@Nullable final MPAlgorithm algorithm, @Nullable final UnsignedInteger counter,
@Nullable final MPResultType resultType, @Nullable final String resultState,
@Nullable final MPResultType loginType, @Nullable final String loginState,
@Nullable final String url, final int uses, final ReadableInstant lastUsed) {
super( name, (algorithm == null)? user.getAlgorithm(): algorithm, counter,
(resultType == null)? user.getDefaultType(): resultType, loginType );
this.user = user;
this.resultState = resultState;
this.loginState = loginState;
this.url = url;
this.uses = uses;
this.lastUsed = lastUsed;
}
@Nullable
public String getUrl() {
return url;
}
public void setUrl(@Nullable final String url) {
this.url = url;
}
public int getUses() {
return uses;
}
public ReadableInstant getLastUsed() {
return lastUsed;
}
public void use() {
uses++;
lastUsed = new Instant();
user.use();
}
public String getResult()
throws MPKeyUnavailableException, MPAlgorithmException {
return getResult( MPKeyPurpose.Authentication, null );
}
public String getResult(final MPKeyPurpose keyPurpose, @Nullable final String keyContext)
throws MPKeyUnavailableException, MPAlgorithmException {
return getResult( keyPurpose, keyContext, getResultState() );
}
public String getLogin()
throws MPKeyUnavailableException, MPAlgorithmException {
return getLogin( getLoginState() );
}
@Nullable
public String getResultState() {
return resultState;
}
public void setSitePassword(final MPResultType resultType, @Nullable final String password)
throws MPKeyUnavailableException, MPAlgorithmException {
setResultType( resultType );
if (password == null)
this.resultState = null;
else
this.resultState = getState(
MPKeyPurpose.Authentication, null, getCounter(), getResultType(), password );
}
@Nullable
public String getLoginState() {
return loginState;
}
public void setLoginName(@Nonnull final MPResultType loginType, @Nullable final String loginName)
throws MPKeyUnavailableException, MPAlgorithmException {
setLoginType( loginType );
if (loginName == null)
this.loginState = null;
else
this.loginState = getState(
MPKeyPurpose.Identification, null, null, getLoginType(), loginName );
}
@Override
public MPFileUser getUser() {
return user;
}
}

View File

@@ -0,0 +1,161 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model.impl;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.*;
import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException;
import com.lyndir.masterpassword.model.MPUser;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.joda.time.Instant;
import org.joda.time.ReadableInstant;
/**
* @author lhunath, 14-12-07
*/
@SuppressWarnings("ComparableImplementedButEqualsNotOverridden")
public class MPFileUser extends MPBasicUser<MPFileSite> {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MPFileUser.class );
@Nullable
private byte[] keyID;
private MPMarshalFormat format;
private MPMarshaller.ContentMode contentMode;
private MPResultType defaultType;
private ReadableInstant lastUsed;
@Nullable
private MPJSONFile json;
public MPFileUser(final String fullName) {
this( fullName, null, MPAlgorithm.Version.CURRENT.getAlgorithm() );
}
public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPAlgorithm algorithm) {
this( fullName, keyID, algorithm, 0, algorithm.mpw_default_result_type(), new Instant(),
MPMarshalFormat.DEFAULT, MPMarshaller.ContentMode.PROTECTED );
}
public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPAlgorithm algorithm,
final int avatar, final MPResultType defaultType, final ReadableInstant lastUsed,
final MPMarshalFormat format, final MPMarshaller.ContentMode contentMode) {
super( avatar, fullName, algorithm );
this.keyID = (keyID == null)? null: keyID.clone();
this.defaultType = defaultType;
this.lastUsed = lastUsed;
this.format = format;
this.contentMode = contentMode;
}
@Nullable
@Override
public byte[] getKeyID() {
return (keyID == null)? null: keyID.clone();
}
@Override
public void setAlgorithm(final MPAlgorithm algorithm) {
if (!algorithm.equals( getAlgorithm() ) && (keyID != null)) {
if (masterKey == null)
throw new IllegalStateException( "Cannot update algorithm when keyID is set but masterKey is unavailable." );
try {
keyID = masterKey.getKeyID( algorithm );
}
catch (final MPKeyUnavailableException e) {
throw new IllegalStateException( "Cannot update algorithm when keyID is set but masterKey is unavailable.", e );
}
catch (final MPAlgorithmException e) {
throw new IllegalStateException( e );
}
}
super.setAlgorithm( algorithm );
}
public MPMarshalFormat getFormat() {
return format;
}
public void setFormat(final MPMarshalFormat format) {
this.format = format;
}
public MPMarshaller.ContentMode getContentMode() {
return contentMode;
}
public void setContentMode(final MPMarshaller.ContentMode contentMode) {
this.contentMode = contentMode;
}
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 void setJSON(final MPJSONFile json) {
this.json = json;
}
@Nonnull
public MPJSONFile getJSON() {
return (json == null)? json = new MPJSONFile(): json;
}
@Override
public void authenticate(final MPMasterKey masterKey)
throws MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException {
super.authenticate( masterKey );
if (keyID == null)
keyID = masterKey.getKeyID( getAlgorithm() );
}
void save()
throws MPKeyUnavailableException, MPAlgorithmException {
MPFileUserManager.get().save( this, getMasterKey() );
}
@Override
public int compareTo(final MPUser<?> o) {
int comparison = (o instanceof MPFileUser)? getLastUsed().compareTo( ((MPFileUser) o).getLastUsed() ): 0;
if (comparison != 0)
return comparison;
return super.compareTo( o );
}
}

View File

@@ -0,0 +1,148 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model.impl;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableList;
import com.google.common.io.CharSink;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.*;
import com.lyndir.masterpassword.model.*;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nonnull;
/**
* Manages user data stored in user-specific {@code .mpsites} files under {@code .mpw.d}.
*
* @author lhunath, 14-12-07
*/
@SuppressWarnings("CallToSystemGetenv")
public class MPFileUserManager extends MPUserManager<MPFileUser> {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MPFileUserManager.class );
private static final MPFileUserManager instance;
static {
String rcDir = System.getenv( MPConstant.env_rcDir );
if (rcDir != null)
instance = create( new File( rcDir ) );
else
instance = create( new File( ifNotNullElseNullable( System.getProperty( "user.home" ), System.getenv( "HOME" ) ), ".mpw.d" ) );
}
private final File path;
public static MPFileUserManager get() {
return instance;
}
public static MPFileUserManager create(final File path) {
return new MPFileUserManager( path );
}
protected MPFileUserManager(final File path) {
super( unmarshallUsers( path ) );
this.path = path;
}
private static Iterable<MPFileUser> unmarshallUsers(final File userFilesDirectory) {
if (!userFilesDirectory.mkdirs() && !userFilesDirectory.isDirectory()) {
logger.err( "Couldn't create directory for user files: %s", userFilesDirectory );
return ImmutableList.of();
}
Map<String, MPFileUser> users = new HashMap<>();
for (final File userFile : listUserFiles( userFilesDirectory ))
for (final MPMarshalFormat format : MPMarshalFormat.values())
if (userFile.getName().endsWith( format.fileSuffix() ))
try {
MPFileUser user = format.unmarshaller().unmarshall( userFile, null );
MPFileUser previousUser = users.put( user.getFullName(), user );
if ((previousUser != null) && (previousUser.getFormat().ordinal() > user.getFormat().ordinal()))
users.put( previousUser.getFullName(), previousUser );
}
catch (final IOException | MPMarshalException e) {
logger.err( e, "Couldn't read user from: %s", userFile );
}
catch (final MPKeyUnavailableException | MPIncorrectMasterPasswordException | MPAlgorithmException e) {
logger.err( e, "Couldn't authenticate user for: %s", userFile );
}
return users.values();
}
private static ImmutableList<File> listUserFiles(final File userFilesDirectory) {
return ImmutableList.copyOf( ifNotNullElse( userFilesDirectory.listFiles( (dir, name) -> {
for (final MPMarshalFormat format : MPMarshalFormat.values())
if (name.endsWith( format.fileSuffix() ))
return true;
return false;
} ), new File[0] ) );
}
@Override
public void deleteUser(final MPFileUser user) {
super.deleteUser( user );
// Remove deleted users.
File userFile = getUserFile( user, user.getFormat() );
if (userFile.exists() && !userFile.delete())
logger.err( "Couldn't delete file: %s", userFile );
}
/**
* Write the current user state to disk.
*/
public void save(final MPFileUser user, final MPMasterKey masterKey)
throws MPKeyUnavailableException, MPAlgorithmException {
try {
MPMarshalFormat format = user.getFormat();
new CharSink() {
@Override
public Writer openStream()
throws IOException {
return new OutputStreamWriter( new FileOutputStream( getUserFile( user, format ) ), Charsets.UTF_8 );
}
}.write( format.marshaller().marshall( user ) );
}
catch (final MPMarshalException | IOException e) {
logger.err( e, "Unable to save sites for user: %s", user );
}
}
@Nonnull
private File getUserFile(final MPUser<?> user, final MPMarshalFormat format) {
return new File( path, user.getFullName() + format.fileSuffix() );
}
/**
* @return The location on the file system where the user models are stored.
*/
public File getPath() {
return path;
}
}

View File

@@ -0,0 +1,85 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model.impl;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
import com.lyndir.masterpassword.MPAlgorithmException;
import com.lyndir.masterpassword.MPKeyUnavailableException;
import com.lyndir.masterpassword.model.MPConstant;
import javax.annotation.Nonnull;
import org.joda.time.Instant;
/**
* @author lhunath, 2017-09-20
*/
@SuppressWarnings({ "HardcodedLineSeparator", "MagicCharacter" })
public class MPFlatMarshaller implements MPMarshaller {
private static final int FORMAT = 1;
@Nonnull
@Override
public String marshall(final MPFileUser user)
throws MPKeyUnavailableException, MPMarshalException, MPAlgorithmException {
StringBuilder content = new StringBuilder();
content.append( "# Master Password site export\n" );
content.append( "# " ).append( user.getContentMode().description() ).append( '\n' );
content.append( "# \n" );
content.append( "##\n" );
content.append( "# Format: " ).append( FORMAT ).append( '\n' );
content.append( "# Date: " ).append( MPConstant.dateTimeFormatter.print( new Instant() ) ).append( '\n' );
content.append( "# User Name: " ).append( user.getFullName() ).append( '\n' );
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( user.getAlgorithm().version().toInt() ).append( '\n' );
content.append( "# Default Type: " ).append( user.getDefaultType().getType() ).append( '\n' );
content.append( "# Passwords: " ).append( user.getContentMode().name() ).append( '\n' );
content.append( "##\n" );
content.append( "#\n" );
content.append( "# Last Times Password Login\t Site\tSite\n" );
content.append( "# used used type name\t name\tpassword\n" );
for (final MPFileSite site : user.getSites()) {
String loginName = site.getLoginState();
String password = site.getResultState();
if (!user.getContentMode().isRedacted()) {
loginName = site.getLogin();
password = site.getResult();
}
content.append( strf( "%s %8d %8s %25s\t%25s\t%s\n", //
MPConstant.dateTimeFormatter.print( site.getLastUsed() ), // lastUsed
site.getUses(), // uses
strf( "%d:%d:%d", //
site.getResultType().getType(), // type
site.getAlgorithm().version().toInt(), // algorithm
site.getCounter().intValue() ), // counter
ifNotNullElse( loginName, "" ), // loginName
site.getName(), // siteName
ifNotNullElse( password, "" ) // password
) );
}
return content.toString();
}
}

View File

@@ -0,0 +1,159 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model.impl;
import com.google.common.base.*;
import com.google.common.io.CharStreams;
import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
import com.lyndir.masterpassword.*;
import com.lyndir.masterpassword.model.MPConstant;
import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException;
import java.io.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.joda.time.Instant;
/**
* @author lhunath, 14-12-07
*/
public class MPFlatUnmarshaller implements MPUnmarshaller {
private static final Logger logger = Logger.get( MPFlatUnmarshaller.class );
private static final Pattern[] unmarshallFormats = {
Pattern.compile( "^([^ ]+) +(\\d+) +(\\d+)(:\\d+)? +([^\t]+)\t(.*)" ),
Pattern.compile( "^([^ ]+) +(\\d+) +(\\d+)(:\\d+)?(:\\d+)? +([^\t]*)\t *([^\t]+)\t(.*)" ) };
private static final Pattern headerFormat = Pattern.compile( "^#\\s*([^:]+): (.*)" );
private static final Pattern colon = Pattern.compile( ":" );
@Nonnull
@Override
public MPFileUser unmarshall(@Nonnull final File file, @Nullable final char[] masterPassword)
throws IOException, MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException {
try (Reader reader = new InputStreamReader( new FileInputStream( file ), Charsets.UTF_8 )) {
return unmarshall( CharStreams.toString( reader ), masterPassword );
}
}
@Nonnull
@Override
public MPFileUser unmarshall(@Nonnull final String content, @Nullable final char[] masterPassword)
throws MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException {
MPFileUser user = null;
byte[] keyID = null;
String fullName = null;
int mpVersion = 0, importFormat = 0, avatar = 0;
boolean clearContent = false, headerStarted = false;
MPResultType defaultType = null;
//noinspection HardcodedLineSeparator
for (final String line : Splitter.on( CharMatcher.anyOf( "\r\n" ) ).omitEmptyStrings().split( content ))
// Header delimitor.
if (line.startsWith( "##" ))
if (!headerStarted)
// Starts the header.
headerStarted = true;
else
// Ends the header.
user = new MPFileUser( fullName, keyID, MPAlgorithm.Version.fromInt( mpVersion ).getAlgorithm(),
avatar, defaultType, new Instant( 0 ), MPMarshalFormat.Flat,
clearContent? MPMarshaller.ContentMode.VISIBLE: MPMarshaller.ContentMode.PROTECTED );
// Comment.
else if (line.startsWith( "#" )) {
if (headerStarted && (user == null)) {
// In header.
Matcher headerMatcher = headerFormat.matcher( line );
if (headerMatcher.matches()) {
String name = headerMatcher.group( 1 ), value = headerMatcher.group( 2 );
if ("Full Name".equalsIgnoreCase( name ) || "User Name".equalsIgnoreCase( name ))
fullName = value;
else if ("Key ID".equalsIgnoreCase( name ))
keyID = CodeUtils.decodeHex( value );
else if ("Algorithm".equalsIgnoreCase( name ))
mpVersion = ConversionUtils.toIntegerNN( value );
else if ("Format".equalsIgnoreCase( name ))
importFormat = ConversionUtils.toIntegerNN( value );
else if ("Avatar".equalsIgnoreCase( name ))
avatar = ConversionUtils.toIntegerNN( value );
else if ("Passwords".equalsIgnoreCase( name ))
clearContent = "visible".equalsIgnoreCase( value );
else if ("Default Type".equalsIgnoreCase( name ))
defaultType = MPResultType.forType( ConversionUtils.toIntegerNN( value ) );
}
}
}
// No comment.
else if (user != null) {
Matcher siteMatcher = unmarshallFormats[importFormat].matcher( line );
if (!siteMatcher.matches()) {
logger.wrn( "Couldn't parse line: %s, skipping.", line );
continue;
}
MPFileSite site;
switch (importFormat) {
case 0:
site = new MPFileSite( user, //
siteMatcher.group( 5 ), MPAlgorithm.Version.fromInt( ConversionUtils.toIntegerNN(
colon.matcher( siteMatcher.group( 4 ) ).replaceAll( "" ) ) ).getAlgorithm(),
user.getAlgorithm().mpw_default_counter(),
MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
clearContent? null: siteMatcher.group( 6 ),
null, null, null, ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ),
MPConstant.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() );
if (clearContent)
site.setSitePassword( site.getResultType(), siteMatcher.group( 6 ) );
break;
case 1:
site = new MPFileSite( user, //
siteMatcher.group( 7 ), MPAlgorithm.Version.fromInt( ConversionUtils.toIntegerNN(
colon.matcher( siteMatcher.group( 4 ) ).replaceAll( "" ) ) ).getAlgorithm(),
UnsignedInteger.valueOf( colon.matcher( siteMatcher.group( 5 ) ).replaceAll( "" ) ),
MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
clearContent? null: siteMatcher.group( 8 ),
MPResultType.GeneratedName, clearContent? null: siteMatcher.group( 6 ), null,
ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ),
MPConstant.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() );
if (clearContent) {
site.setSitePassword( site.getResultType(), siteMatcher.group( 8 ) );
site.setLoginName( MPResultType.StoredPersonal, siteMatcher.group( 6 ) );
}
break;
default:
throw new MPMarshalException( "Unexpected format: " + importFormat );
}
user.addSite( site );
}
if (user == null)
throw new MPMarshalException( "No full header found in import file." );
return user;
}
}

View File

@@ -0,0 +1,38 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model.impl;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import java.util.*;
/**
* @author lhunath, 2018-05-14
*/
class MPJSONAnyObject {
@JsonAnySetter
final Map<String, Object> any = new LinkedHashMap<>();
@JsonAnyGetter
public Map<String, Object> getAny() {
return Collections.unmodifiableMap( any );
}
}

View File

@@ -0,0 +1,244 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model.impl;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.masterpassword.*;
import com.lyndir.masterpassword.model.MPConstant;
import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.annotation.Nullable;
import org.joda.time.Instant;
/**
* @author lhunath, 2018-04-27
*/
@SuppressFBWarnings( "URF_UNREAD_FIELD" )
public class MPJSONFile extends MPJSONAnyObject {
protected static final ObjectMapper objectMapper = new ObjectMapper();
static {
objectMapper.setSerializationInclusion( JsonInclude.Include.NON_EMPTY );
objectMapper.setVisibility( PropertyAccessor.FIELD, JsonAutoDetect.Visibility.NON_PRIVATE );
}
public MPJSONFile write(final MPFileUser modelUser)
throws MPKeyUnavailableException, MPAlgorithmException {
// Section: "export"
if (export == null)
export = new Export();
export.format = 1;
export.redacted = modelUser.getContentMode().isRedacted();
export.date = MPConstant.dateTimeFormatter.print( new Instant() );
// Section: "user"
if (user == null)
user = new User();
user.avatar = modelUser.getAvatar();
user.full_name = modelUser.getFullName();
user.last_used = MPConstant.dateTimeFormatter.print( modelUser.getLastUsed() );
user.key_id = modelUser.exportKeyID();
user.algorithm = modelUser.getAlgorithm().version();
user.default_type = modelUser.getDefaultType();
// Section "sites"
if (sites == null)
sites = new LinkedHashMap<>();
for (final MPFileSite modelSite : modelUser.getSites()) {
String content = null, loginContent = null;
if (!export.redacted) {
// Clear Text
content = modelSite.getResult();
loginContent = modelUser.getMasterKey().siteResult(
modelSite.getName(), modelSite.getAlgorithm(), modelSite.getAlgorithm().mpw_default_counter(),
MPKeyPurpose.Identification, null, modelSite.getLoginType(), modelSite.getLoginState() );
} else {
// Redacted
if (modelSite.getResultType().supportsTypeFeature( MPSiteFeature.ExportContent ))
content = modelSite.getResultState();
if (modelSite.getLoginType().supportsTypeFeature( MPSiteFeature.ExportContent ))
loginContent = modelSite.getLoginState();
}
Site site = sites.get( modelSite.getName() );
if (site == null)
sites.put( modelSite.getName(), site = new Site() );
site.type = modelSite.getResultType();
site.counter = modelSite.getCounter().longValue();
site.algorithm = modelSite.getAlgorithm().version();
site.password = content;
site.login_name = loginContent;
site.login_type = modelSite.getLoginType();
site.uses = modelSite.getUses();
site.last_used = MPConstant.dateTimeFormatter.print( modelSite.getLastUsed() );
if (site._ext_mpw == null)
site._ext_mpw = new Site.Ext();
site._ext_mpw.url = modelSite.getUrl();
if (site.questions == null)
site.questions = new LinkedHashMap<>();
// for (size_t q = 0; q < site.questions_count; ++q) {
// MPMarshalledQuestion *question = &site.questions[q];
// if (!question.keyword)
// continue;
//
// json_object *json_site_question = json_object_new_object();
// json_object_object_add( json_site_questions, question.keyword, json_site_question );
// json_object_object_add( json_site_question, "type = question.type;
//
// if (!user.redacted) {
// // Clear Text
// const char *answerContent = mpw_siteResult( masterKey, site.name, MPCounterValueInitial,
// MPKeyPurposeRecovery, question.keyword, question.type, question.content, site.algorithm );
// json_object_object_add( json_site_question, "answer = answerContent;
// }
// else {
// // Redacted
// if (site.type & MPSiteFeatureExportContent && question.content && strlen( question.content ))
// json_object_object_add( json_site_question, "answer = question.content;
// }
// }
// json_object *json_site_mpw = json_object_new_object();
// fileSite._ext_mpw = json_site_mpw;
// if (site.url)
// json_object_object_add( json_site_mpw, "url", site.url );
}
return this;
}
public MPFileUser read(@Nullable final char[] masterPassword)
throws MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException {
MPAlgorithm algorithm = ifNotNullElse( user.algorithm, MPAlgorithm.Version.CURRENT ).getAlgorithm();
MPFileUser model = new MPFileUser(
user.full_name, CodeUtils.decodeHex( user.key_id ), algorithm, user.avatar,
(user.default_type != null)? user.default_type: algorithm.mpw_default_result_type(),
(user.last_used != null)? MPConstant.dateTimeFormatter.parseDateTime( user.last_used ): new Instant(),
MPMarshalFormat.JSON, export.redacted? MPMarshaller.ContentMode.PROTECTED: MPMarshaller.ContentMode.VISIBLE );
model.setJSON( this );
if (masterPassword != null)
model.authenticate( masterPassword );
for (final Map.Entry<String, Site> siteEntry : sites.entrySet()) {
String siteName = siteEntry.getKey();
Site fileSite = siteEntry.getValue();
MPFileSite site = new MPFileSite(
model, siteName, fileSite.algorithm.getAlgorithm(), UnsignedInteger.valueOf( fileSite.counter ), fileSite.type,
export.redacted? fileSite.password: null,
fileSite.login_type, export.redacted? fileSite.login_name: null,
(fileSite._ext_mpw != null)? fileSite._ext_mpw.url: null, fileSite.uses,
(fileSite.last_used != null)? MPConstant.dateTimeFormatter.parseDateTime( fileSite.last_used ): new Instant() );
if (!export.redacted) {
if (fileSite.password != null)
site.setSitePassword( (fileSite.type != null)? fileSite.type: MPResultType.StoredPersonal, fileSite.password );
if (fileSite.login_name != null)
site.setLoginName( (fileSite.login_type != null)? fileSite.login_type: MPResultType.StoredPersonal,
fileSite.login_name );
}
model.addSite( site );
}
return model;
}
// -- Data
Export export;
User user;
Map<String, Site> sites;
public static class Export extends MPJSONAnyObject {
int format;
boolean redacted;
@Nullable
String date;
}
public static class User extends MPJSONAnyObject {
int avatar;
String full_name;
String last_used;
@Nullable
MPAlgorithm.Version algorithm;
@Nullable
String key_id;
@Nullable
MPResultType default_type;
}
public static class Site extends MPJSONAnyObject {
long counter;
MPAlgorithm.Version algorithm;
@Nullable
MPResultType type;
@Nullable
String password;
@Nullable
MPResultType login_type;
@Nullable
String login_name;
int uses;
@Nullable
String last_used;
@Nullable
Map<String, Question> questions;
@Nullable
Ext _ext_mpw;
public static class Ext extends MPJSONAnyObject {
@Nullable
String url;
}
public static class Question extends MPJSONAnyObject {
@Nullable
MPResultType type;
@Nullable
String answer;
}
}
}

View File

@@ -0,0 +1,46 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model.impl;
import static com.lyndir.masterpassword.model.impl.MPJSONFile.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.lyndir.masterpassword.MPAlgorithmException;
import com.lyndir.masterpassword.MPKeyUnavailableException;
import javax.annotation.Nonnull;
/**
* @author lhunath, 2017-09-20
*/
public class MPJSONMarshaller implements MPMarshaller {
@Nonnull
@Override
public String marshall(final MPFileUser user)
throws MPKeyUnavailableException, MPMarshalException, MPAlgorithmException {
try {
return objectMapper.writeValueAsString( user.getJSON().write( user ) );
}
catch (final JsonProcessingException e) {
throw new MPMarshalException( "Couldn't compose JSON for: " + user, e );
}
}
}

View File

@@ -0,0 +1,73 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model.impl;
import static com.lyndir.masterpassword.model.impl.MPJSONFile.*;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.lyndir.masterpassword.MPAlgorithmException;
import com.lyndir.masterpassword.MPKeyUnavailableException;
import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException;
import java.io.File;
import java.io.IOException;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* @author lhunath, 2017-09-20
*/
public class MPJSONUnmarshaller implements MPUnmarshaller {
@Nonnull
@Override
public MPFileUser unmarshall(@Nonnull final File file, @Nullable final char[] masterPassword)
throws IOException, MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException {
try {
return objectMapper.readValue( file, MPJSONFile.class ).read( masterPassword );
}
catch (final JsonParseException e) {
throw new MPMarshalException( "Couldn't parse JSON in: " + file, e );
}
catch (final JsonMappingException e) {
throw new MPMarshalException( "Couldn't map JSON in: " + file, e );
}
}
@Nonnull
@Override
public MPFileUser unmarshall(@Nonnull final String content, @Nullable final char[] masterPassword)
throws MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException {
try {
return objectMapper.readValue( content, MPJSONFile.class ).read( masterPassword );
}
catch (final JsonParseException e) {
throw new MPMarshalException( "Couldn't parse JSON.", e );
}
catch (final JsonMappingException e) {
throw new MPMarshalException( "Couldn't map JSON.", e );
}
catch (final IOException e) {
throw new MPMarshalException( "Couldn't read JSON.", e );
}
}
}

View File

@@ -0,0 +1,36 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model.impl;
import com.lyndir.masterpassword.MPException;
/**
* @author lhunath, 2018-04-26
*/
public class MPMarshalException extends MPException {
public MPMarshalException(final String message) {
super( message );
}
public MPMarshalException(final String message, final Throwable cause) {
super( message, cause );
}
}

View File

@@ -0,0 +1,75 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model.impl;
/**
* @author lhunath, 2017-09-20
*
* This enum is ordered from oldest to newest format, the latest being most preferred.
*/
public enum MPMarshalFormat {
/**
* Marshal using the line-based plain-text format.
*/
Flat {
@Override
public MPMarshaller marshaller() {
return new MPFlatMarshaller();
}
@Override
public MPUnmarshaller unmarshaller() {
return new MPFlatUnmarshaller();
}
@Override
public String fileSuffix() {
return ".mpsites";
}
},
/**
* Marshal using the JSON structured format.
*/
JSON {
@Override
public MPMarshaller marshaller() {
return new MPJSONMarshaller();
}
@Override
public MPUnmarshaller unmarshaller() {
return new MPJSONUnmarshaller();
}
@Override
public String fileSuffix() {
return ".mpsites.json";
}
};
public static final MPMarshalFormat DEFAULT = JSON;
public abstract MPMarshaller marshaller();
public abstract MPUnmarshaller unmarshaller();
@SuppressWarnings("MethodReturnAlwaysConstant")
public abstract String fileSuffix();
}

View File

@@ -0,0 +1,56 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model.impl;
import com.lyndir.masterpassword.MPAlgorithmException;
import com.lyndir.masterpassword.MPKeyUnavailableException;
import javax.annotation.Nonnull;
/**
* @author lhunath, 14-12-07
*/
@FunctionalInterface
public interface MPMarshaller {
@Nonnull
String marshall(MPFileUser user)
throws MPKeyUnavailableException, MPMarshalException, MPAlgorithmException;
enum ContentMode {
PROTECTED( "Export of site names and stored passwords (unless device-private) encrypted with the master key.", true ),
VISIBLE( "Export of site names and passwords in clear-text.", false );
private final String description;
private final boolean redacted;
ContentMode(final String description, final boolean redacted) {
this.description = description;
this.redacted = redacted;
}
public String description() {
return description;
}
public boolean isRedacted() {
return redacted;
}
}
}

View File

@@ -0,0 +1,42 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model.impl;
import com.lyndir.masterpassword.MPAlgorithmException;
import com.lyndir.masterpassword.MPKeyUnavailableException;
import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException;
import java.io.File;
import java.io.IOException;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* @author lhunath, 14-12-07
*/
public interface MPUnmarshaller {
@Nonnull
MPFileUser unmarshall(@Nonnull File file, @Nullable char[] masterPassword)
throws IOException, MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException;
@Nonnull
MPFileUser unmarshall(@Nonnull String content, @Nullable char[] masterPassword)
throws MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException;
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
//==============================================================================
/**
* @author lhunath, 2018-05-14
*/
@ParametersAreNonnullByDefault
package com.lyndir.masterpassword.model.impl;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@@ -0,0 +1,26 @@
//==============================================================================
// 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 <http://www.gnu.org/licenses/>.
//==============================================================================
/**
* @author lhunath, 15-02-04
*/
@ParametersAreNonnullByDefault
package com.lyndir.masterpassword.model;
import javax.annotation.ParametersAreNonnullByDefault;