Java relocation.
This commit is contained in:
24
platform-independent/java/model/build.gradle
Normal file
24
platform-independent/java/model/build.gradle
Normal 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()
|
@@ -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();
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
@@ -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();
|
||||
}
|
@@ -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() );
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
@@ -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() );
|
||||
}
|
||||
}
|
@@ -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() );
|
||||
}
|
||||
}
|
@@ -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() );
|
||||
}
|
||||
}
|
@@ -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() );
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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 );
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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 );
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
@@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
@@ -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 );
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
@@ -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;
|
@@ -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;
|
Reference in New Issue
Block a user