mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-25 04:17:59 +01:00
Port wot-dijkstra Java classes to Kotlin
This commit is contained in:
parent
31aeb18535
commit
9d3a9893f2
21 changed files with 395 additions and 824 deletions
|
@ -42,7 +42,7 @@ allprojects {
|
|||
}
|
||||
|
||||
// For library modules, enable android api compatibility check
|
||||
if (it.name != 'pgpainless-cli') {
|
||||
if (it.name != 'pgpainless-cli' && it.name != 'wot-dijkstra') {
|
||||
// animalsniffer
|
||||
apply plugin: 'ru.vyarus.animalsniffer'
|
||||
dependencies {
|
||||
|
|
|
@ -14,7 +14,6 @@ import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
|
|||
import org.pgpainless.wot.dijkstra.sq.CertSynopsis;
|
||||
import org.pgpainless.wot.dijkstra.sq.Certification;
|
||||
import org.pgpainless.wot.dijkstra.sq.Depth;
|
||||
import org.pgpainless.wot.dijkstra.sq.Optional;
|
||||
import org.pgpainless.wot.dijkstra.sq.RegexSet;
|
||||
|
||||
/**
|
||||
|
@ -34,7 +33,7 @@ public class CertificationFactory {
|
|||
public static Certification fromDelegation(CertSynopsis issuer,
|
||||
CertSynopsis target,
|
||||
PGPSignature signature) {
|
||||
return fromSignature(issuer, Optional.empty(), target, signature);
|
||||
return fromSignature(issuer, null, target, signature);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,7 +49,7 @@ public class CertificationFactory {
|
|||
String targetUserId,
|
||||
CertSynopsis target,
|
||||
PGPSignature signature) {
|
||||
return fromSignature(issuer, Optional.just(targetUserId), target, signature);
|
||||
return fromSignature(issuer, targetUserId, target, signature);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -63,7 +62,7 @@ public class CertificationFactory {
|
|||
* @return certification
|
||||
*/
|
||||
public static Certification fromSignature(CertSynopsis issuer,
|
||||
Optional<String> targetUserId,
|
||||
String targetUserId,
|
||||
CertSynopsis target,
|
||||
PGPSignature signature) {
|
||||
return new Certification(
|
||||
|
@ -71,7 +70,7 @@ public class CertificationFactory {
|
|||
target,
|
||||
targetUserId,
|
||||
SignatureSubpacketsUtil.getSignatureCreationTime(signature).getTime(),
|
||||
Optional.maybe(SignatureSubpacketsUtil.getSignatureExpirationTimeAsDate(signature)),
|
||||
SignatureSubpacketsUtil.getSignatureExpirationTimeAsDate(signature),
|
||||
SignatureSubpacketsUtil.isExportable(signature),
|
||||
getTrustAmountFrom(signature),
|
||||
getTrustDepthFrom(signature),
|
||||
|
|
|
@ -33,7 +33,6 @@ import org.pgpainless.wot.dijkstra.sq.CertSynopsis;
|
|||
import org.pgpainless.wot.dijkstra.sq.Certification;
|
||||
import org.pgpainless.wot.dijkstra.sq.CertificationSet;
|
||||
import org.pgpainless.wot.dijkstra.sq.Network;
|
||||
import org.pgpainless.wot.dijkstra.sq.Optional;
|
||||
import org.pgpainless.wot.dijkstra.sq.ReferenceTime;
|
||||
import org.pgpainless.wot.sugar.IterableIterator;
|
||||
import org.pgpainless.wot.sugar.PrefixedIterator;
|
||||
|
@ -80,7 +79,7 @@ public class WebOfTrust implements CertificateAuthority {
|
|||
Iterator<Certificate> certificates = certificateStore.items();
|
||||
Iterator<Certificate> withTrustRoot = new PrefixedIterator<>(trustRoot, certificates);
|
||||
IterableIterator<Certificate> iterable = new IterableIterator<>(withTrustRoot);
|
||||
network = fromCertificates(iterable, PGPainless.getPolicy(), Optional.just(ReferenceTime.now()));
|
||||
network = fromCertificates(iterable, PGPainless.getPolicy(), ReferenceTime.now());
|
||||
}
|
||||
|
||||
|
||||
|
@ -95,9 +94,9 @@ public class WebOfTrust implements CertificateAuthority {
|
|||
public static Network fromCertificates(
|
||||
Iterable<Certificate> certificates,
|
||||
Policy policy,
|
||||
Optional<ReferenceTime> optReferenceTime) {
|
||||
ReferenceTime optReferenceTime) {
|
||||
|
||||
ReferenceTime referenceTime = optReferenceTime.isPresent() ? optReferenceTime.get() : ReferenceTime.now();
|
||||
ReferenceTime referenceTime = optReferenceTime == null ? ReferenceTime.now() : optReferenceTime;
|
||||
List<KeyRingInfo> validCerts = parseValidCertificates(certificates, policy, referenceTime.getTimestamp());
|
||||
|
||||
LOGGER.debug("Successfully parsed " + validCerts.size() + " certificates.");
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.wot.dijkstra;
|
||||
|
||||
public final class IntegerUtils {
|
||||
|
||||
private IntegerUtils() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Backported method from Java 8.
|
||||
*
|
||||
* @param x x
|
||||
* @param y y
|
||||
* @return result of comparison
|
||||
*/
|
||||
public static int compare(int x, int y) {
|
||||
// noinspection UseCompareMethod
|
||||
return x < y ? -1 : (x == y ? 0 : 1);
|
||||
}
|
||||
}
|
|
@ -1,148 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.wot.dijkstra.sq;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class Certification {
|
||||
|
||||
private final CertSynopsis issuer;
|
||||
private final CertSynopsis target;
|
||||
private final Optional<String> userId;
|
||||
|
||||
private final Date creationTime;
|
||||
private final Optional<Date> expirationTime;
|
||||
private final boolean exportable;
|
||||
private final int trustAmount;
|
||||
private final Depth trustDepth;
|
||||
private final RegexSet regex;
|
||||
|
||||
public Certification(
|
||||
CertSynopsis issuer,
|
||||
CertSynopsis target,
|
||||
Optional<String> userId,
|
||||
Date creationTime,
|
||||
Optional<Date> expirationTime,
|
||||
boolean exportable,
|
||||
int trustAmount,
|
||||
Depth trustDepth,
|
||||
RegexSet regex) {
|
||||
this.issuer = issuer;
|
||||
this.target = target;
|
||||
this.userId = userId;
|
||||
this.creationTime = creationTime;
|
||||
this.expirationTime = expirationTime;
|
||||
this.exportable = exportable;
|
||||
this.trustAmount = trustAmount;
|
||||
this.trustDepth = trustDepth;
|
||||
this.regex = regex;
|
||||
}
|
||||
|
||||
public Certification(CertSynopsis issuer,
|
||||
Optional<String> targetUserId,
|
||||
CertSynopsis target,
|
||||
Date creationTime) {
|
||||
this.issuer = issuer;
|
||||
this.target = target;
|
||||
this.userId = targetUserId;
|
||||
this.creationTime = creationTime;
|
||||
|
||||
this.expirationTime = Optional.empty();
|
||||
this.exportable = true;
|
||||
this.trustDepth = Depth.limited(0);
|
||||
this.trustAmount = 120;
|
||||
this.regex = RegexSet.wildcard();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the issuer of the certification.
|
||||
*
|
||||
* @return issuer
|
||||
*/
|
||||
public CertSynopsis getIssuer() {
|
||||
return issuer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the target of the certification.
|
||||
*
|
||||
* @return target
|
||||
*/
|
||||
public CertSynopsis getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the target user-id.
|
||||
*
|
||||
* @return user-id
|
||||
*/
|
||||
public Optional<String> getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the creation time of the certification.
|
||||
*
|
||||
* @return creation time
|
||||
*/
|
||||
public Date getCreationTime() {
|
||||
return creationTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the (optional) expiration time of the certification.
|
||||
*
|
||||
* @return optional expiration time
|
||||
*/
|
||||
public Optional<Date> getExpirationTime() {
|
||||
return expirationTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the certification is marked as exportable.
|
||||
*
|
||||
* @return exportable
|
||||
*/
|
||||
public boolean isExportable() {
|
||||
return exportable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the trust amount of the certification.
|
||||
*
|
||||
* @return trust amount
|
||||
*/
|
||||
public int getTrustAmount() {
|
||||
return trustAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the trust depth of the certification.
|
||||
*
|
||||
* @return trust depth
|
||||
*/
|
||||
public Depth getTrustDepth() {
|
||||
return trustDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the set of regular expressions.
|
||||
*
|
||||
* @return regex set
|
||||
*/
|
||||
public RegexSet getRegexes() {
|
||||
return regex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(issuer.getFingerprint()).append((issuer.getUserIds().isEmpty() ? " " : " (" + issuer.getUserIds().keySet().iterator().next() + ") "));
|
||||
sb.append(userId.isPresent() ? "certifies" : "delegates to").append(userId.isPresent() ? " [" + userId.get() + "] " : " ").append(target.getFingerprint())
|
||||
.append(userId.isEmpty() && !target.getUserIds().isEmpty() ? " (" + target.getUserIds().keySet().iterator().next() + ")" : "");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -1,127 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.wot.dijkstra.sq;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* A {@link CertificationSet} is a set of {@link Certification Certifications} made by the same issuer, on the same
|
||||
* target certificate.
|
||||
* In some sense, a {@link CertificationSet} can be considered an edge in the web of trust.
|
||||
*/
|
||||
public final class CertificationSet {
|
||||
|
||||
private final CertSynopsis issuer;
|
||||
private final CertSynopsis target;
|
||||
|
||||
private final Map<Optional<String>, List<Certification>> certifications;
|
||||
|
||||
/**
|
||||
* Create an empty {@link CertificationSet}.
|
||||
*
|
||||
* @param issuer issuer
|
||||
* @param target target
|
||||
* @return empty set
|
||||
*/
|
||||
public static CertificationSet empty(CertSynopsis issuer, CertSynopsis target) {
|
||||
return new CertificationSet(issuer, target, new HashMap<>());
|
||||
}
|
||||
|
||||
public static CertificationSet fromCertification(Certification certification) {
|
||||
CertificationSet set = CertificationSet.empty(certification.getIssuer(), certification.getTarget());
|
||||
set.add(certification);
|
||||
return set;
|
||||
}
|
||||
|
||||
private CertificationSet(CertSynopsis issuer,
|
||||
CertSynopsis target,
|
||||
Map<Optional<String>, List<Certification>> certifications) {
|
||||
this.issuer = issuer;
|
||||
this.target = target;
|
||||
this.certifications = new HashMap<>(certifications);
|
||||
}
|
||||
|
||||
public CertSynopsis getIssuer() {
|
||||
return issuer;
|
||||
}
|
||||
|
||||
public CertSynopsis getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
public Map<Optional<String>, List<Certification>> getCertifications() {
|
||||
// Copy to avoid side effects
|
||||
Map<Optional<String>, List<Certification>> copy = new HashMap<>();
|
||||
for (Optional<String> key : certifications.keySet()) {
|
||||
copy.put(key, new ArrayList<>(certifications.get(key)));
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge this {@link CertificationSet} with another instance.
|
||||
* After the operation, this will contain {@link Certification Certifications} from both sets.
|
||||
*
|
||||
* @param other other {@link CertificationSet}
|
||||
*/
|
||||
public void merge(@Nonnull CertificationSet other) {
|
||||
if (other == this) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!issuer.getFingerprint().equals(other.issuer.getFingerprint())) {
|
||||
throw new IllegalArgumentException("Issuer fingerprint mismatch.");
|
||||
}
|
||||
|
||||
if (!target.getFingerprint().equals(other.target.getFingerprint())) {
|
||||
throw new IllegalArgumentException("Target fingerprint mismatch.");
|
||||
}
|
||||
|
||||
for (Map.Entry<Optional<String>, List<Certification>> entry : other.certifications.entrySet()) {
|
||||
for (Certification certification : entry.getValue()) {
|
||||
add(certification);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a {@link Certification} into this {@link CertificationSet}.
|
||||
*
|
||||
* @param certification certification
|
||||
*/
|
||||
public void add(@Nonnull Certification certification) {
|
||||
if (!issuer.getFingerprint().equals(certification.getIssuer().getFingerprint())) {
|
||||
throw new IllegalArgumentException("Issuer fingerprint mismatch.");
|
||||
}
|
||||
if (!target.getFingerprint().equals(certification.getTarget().getFingerprint())) {
|
||||
throw new IllegalArgumentException("Target fingerprint mismatch.");
|
||||
}
|
||||
|
||||
List<Certification> certificationsForUserId = certifications.get(certification.getUserId());
|
||||
// noinspection Java8MapApi
|
||||
if (certificationsForUserId == null) {
|
||||
certificationsForUserId = new ArrayList<>();
|
||||
certifications.put(certification.getUserId(), certificationsForUserId);
|
||||
}
|
||||
// TODO: Prevent duplicates, only keep newest timestamped sig?
|
||||
certificationsForUserId.add(certification);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Map.Entry<Optional<String>, List<Certification>> entry : certifications.entrySet()) {
|
||||
for (Certification certification : entry.getValue()) {
|
||||
sb.append(certification).append('\n');
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.wot.dijkstra.sq;
|
||||
|
||||
import org.pgpainless.wot.dijkstra.IntegerUtils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public final class Depth implements Comparable<Depth> {
|
||||
|
||||
private final Optional<Integer> depth;
|
||||
|
||||
private Depth(Optional<Integer> depth) {
|
||||
this.depth = depth;
|
||||
if (!isUnconstrained() && (getLimit().get() < 0 || getLimit().get() > 255)) {
|
||||
throw new IllegalArgumentException("Depth must be a value between 0 and 255");
|
||||
}
|
||||
}
|
||||
|
||||
public static Depth unconstrained() {
|
||||
return new Depth(Optional.empty());
|
||||
}
|
||||
|
||||
public static Depth limited(int depth) {
|
||||
return new Depth(Optional.just(depth));
|
||||
}
|
||||
|
||||
public static Depth auto(int depth) {
|
||||
return depth == 255 ? unconstrained() : limited(depth);
|
||||
}
|
||||
|
||||
public Optional<Integer> getLimit() {
|
||||
return depth;
|
||||
}
|
||||
|
||||
public boolean isUnconstrained() {
|
||||
return getLimit().isEmpty();
|
||||
}
|
||||
|
||||
public Depth decrease(int value) {
|
||||
if (isUnconstrained()) {
|
||||
return unconstrained();
|
||||
}
|
||||
if (getLimit().get() >= value) {
|
||||
return limited(getLimit().get() - value);
|
||||
}
|
||||
throw new IllegalArgumentException("Depth cannot be decreased.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@Nonnull Depth o) {
|
||||
if (isUnconstrained()) {
|
||||
if (o.isUnconstrained()) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (o.isUnconstrained()) {
|
||||
return -1;
|
||||
} else {
|
||||
return IntegerUtils.compare(getLimit().get(), o.getLimit().get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return isUnconstrained() ? "unconstrained" : getLimit().get().toString();
|
||||
}
|
||||
|
||||
public Depth min(Depth other) {
|
||||
if (compareTo(other) <= 0) {
|
||||
return this;
|
||||
} else {
|
||||
return other;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,156 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.wot.dijkstra.sq;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.pgpainless.key.OpenPgpFingerprint;
|
||||
|
||||
/**
|
||||
* A network consists of nodes, and edges between them.
|
||||
* For the Web of Trust, nodes consist of {@link CertSynopsis CertSynopses}, while the edges between the nodes are
|
||||
* {@link CertificationSet CertificationSets}.
|
||||
* Edges can hereby be accessed in two ways:
|
||||
* <ul>
|
||||
* <li>{@link #getEdges()} returns a {@link Map} keyed by the {@link OpenPgpFingerprint fingerprint} of an issuer,
|
||||
* whose values are {@link List Lists} containing all edges originating from the issuer.</li>
|
||||
* <li>{@link #getReverseEdges()} on the other hand returns a {@link Map} keyed by the
|
||||
* {@link OpenPgpFingerprint fingerprint} of a target, whose value are {@link List Lists} containing all edges
|
||||
* pointing to the target.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class Network {
|
||||
|
||||
private final Map<OpenPgpFingerprint, CertSynopsis> nodes;
|
||||
private final Map<OpenPgpFingerprint, List<CertificationSet>> edges;
|
||||
private final Map<OpenPgpFingerprint, List<CertificationSet>> reverseEdges;
|
||||
private final ReferenceTime referenceTime;
|
||||
|
||||
/**
|
||||
* Create a {@link Network} from a set of nodes, edges, reversed edges and a reference time.
|
||||
*
|
||||
* @param nodes map containing all nodes of the network, keyed by their fingerprints
|
||||
* @param edges map containing all edges of the network, keyed by the fingerprint of the issuer
|
||||
* @param reverseEdges map containing all reversed edges of the network, keyed by the fingerprint of the target
|
||||
* @param referenceTime reference time
|
||||
*/
|
||||
public Network(Map<OpenPgpFingerprint, CertSynopsis> nodes,
|
||||
Map<OpenPgpFingerprint, List<CertificationSet>> edges,
|
||||
Map<OpenPgpFingerprint, List<CertificationSet>> reverseEdges,
|
||||
ReferenceTime referenceTime) {
|
||||
this.nodes = nodes;
|
||||
this.edges = edges;
|
||||
this.reverseEdges = reverseEdges;
|
||||
this.referenceTime = referenceTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an empty {@link Network}.
|
||||
*
|
||||
* @param referenceTime reference time for evaluation
|
||||
* @return network
|
||||
*/
|
||||
public static Network empty(@Nonnull ReferenceTime referenceTime) {
|
||||
return new Network(
|
||||
new HashMap<>(),
|
||||
new HashMap<>(),
|
||||
new HashMap<>(),
|
||||
referenceTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all nodes ({@link CertSynopsis}) of the {@link Network}, indexed by their
|
||||
* {@link OpenPgpFingerprint fingerprints}.
|
||||
*
|
||||
* @return nodes of the network
|
||||
*/
|
||||
public Map<OpenPgpFingerprint, CertSynopsis> getNodes() {
|
||||
return new HashMap<>(nodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all edges of the {@link Network}, indexed by the {@link OpenPgpFingerprint fingerprint} of the issuer.
|
||||
* An edge consists of a {@link CertificationSet} containing all signatures made by the issuer on the target.
|
||||
* TODO: Do we care about immutability, or can we ignore that issue since Network is only used by
|
||||
* WebOfTrust anyways?
|
||||
*
|
||||
* @return map of edges
|
||||
*/
|
||||
public Map<OpenPgpFingerprint, List<CertificationSet>> getEdges() {
|
||||
return new HashMap<>(edges);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all reversed edges of the {@link Network}, indexed by the {@link OpenPgpFingerprint fingerprint} of the target.
|
||||
* TODO: Do we care about immutability, or can we ignore that issue since Network is only used by
|
||||
* WebOfTrust anyways?
|
||||
*
|
||||
* @return map of reversed edges
|
||||
*/
|
||||
public Map<OpenPgpFingerprint, List<CertificationSet>> getReverseEdges() {
|
||||
return new HashMap<>(reverseEdges);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total number of edges on the network.
|
||||
*
|
||||
* @return number of edges
|
||||
*/
|
||||
public int getNumberOfEdges() {
|
||||
int num = 0;
|
||||
for (List<CertificationSet> outEdges : edges.values()) {
|
||||
num += outEdges.size();
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total number of signatures the network comprises.
|
||||
*
|
||||
* @return number of signatures
|
||||
*/
|
||||
public int getNumberOfSignatures() {
|
||||
int num = 0;
|
||||
for (List<CertificationSet> edgesPerIssuer : edges.values()) {
|
||||
for (CertificationSet edge : edgesPerIssuer) {
|
||||
for (List<Certification> sigsPerDatum : edge.getCertifications().values()) {
|
||||
num += sigsPerDatum.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link ReferenceTime} which was used when creating the {@link Network}.
|
||||
*
|
||||
* @return reference time
|
||||
*/
|
||||
public ReferenceTime getReferenceTime() {
|
||||
return referenceTime;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
int edgeNum = 0;
|
||||
for (List<CertificationSet> edgesFrom : edges.values()) {
|
||||
edgeNum += edgesFrom.size();
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder("Network with " + getNodes().size() + " nodes, " + edgeNum + " edges:\n");
|
||||
for (OpenPgpFingerprint issuer : getNodes().keySet()) {
|
||||
List<CertificationSet> edges = getEdges().get(issuer);
|
||||
if (edges == null) {
|
||||
continue;
|
||||
}
|
||||
for (CertificationSet edge : edges) {
|
||||
sb.append(edge);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.wot.dijkstra.sq;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public final class Optional<T> {
|
||||
|
||||
private final T item;
|
||||
|
||||
public static <T> Optional<T> empty() {
|
||||
return new Optional<>();
|
||||
}
|
||||
|
||||
public static <T> Optional<T> just(@Nonnull T item) {
|
||||
return new Optional<>(item);
|
||||
}
|
||||
|
||||
public static <T> Optional<T> maybe(@Nullable T item) {
|
||||
return item == null ? empty() : just(item);
|
||||
}
|
||||
|
||||
private Optional() {
|
||||
this.item = null;
|
||||
}
|
||||
|
||||
private Optional(@Nonnull T item) {
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return item == null;
|
||||
}
|
||||
|
||||
public boolean isPresent() {
|
||||
return item != null;
|
||||
}
|
||||
|
||||
public @Nonnull T get() {
|
||||
if (item == null) {
|
||||
throw new NullPointerException("Item is null.");
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj instanceof Optional)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Optional other = (Optional) obj;
|
||||
if (isEmpty() && other.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isPresent() && isPresent()) {
|
||||
return get().equals(other.get());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.wot.dijkstra.sq;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Path {
|
||||
|
||||
private CertSynopsis root;
|
||||
private List<Certification> edges;
|
||||
private Depth residualDepth;
|
||||
|
||||
public Path(CertSynopsis root) {
|
||||
this.root = root;
|
||||
this.edges = new ArrayList<>();
|
||||
this.residualDepth = Depth.unconstrained();
|
||||
}
|
||||
|
||||
public CertSynopsis getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
public CertSynopsis getTarget() {
|
||||
if (edges.isEmpty()) {
|
||||
return getRoot();
|
||||
} else {
|
||||
return edges.get(edges.size() - 1).getTarget();
|
||||
}
|
||||
}
|
||||
|
||||
public List<CertSynopsis> getCertificates() {
|
||||
List<CertSynopsis> certs = new ArrayList<>();
|
||||
certs.add(getRoot());
|
||||
for (Certification edge : edges) {
|
||||
certs.add(edge.getTarget());
|
||||
}
|
||||
return certs;
|
||||
}
|
||||
|
||||
public int getLength() {
|
||||
return edges.size() + 1;
|
||||
}
|
||||
|
||||
public List<Certification> getCertifications() {
|
||||
return new ArrayList<>(edges);
|
||||
}
|
||||
|
||||
public Depth getResidualDepth() {
|
||||
return residualDepth;
|
||||
}
|
||||
|
||||
public int getAmount() {
|
||||
if (edges.isEmpty()) {
|
||||
return 120;
|
||||
}
|
||||
int min = 255;
|
||||
for (Certification edge : edges) {
|
||||
min = Math.min(min, edge.getTrustAmount());
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
public void append(Certification certification) {
|
||||
if (!getTarget().getFingerprint().equals(certification.getIssuer().getFingerprint())) {
|
||||
throw new IllegalArgumentException("Cannot append certification to path: Path's tail is not issuer of the certification.");
|
||||
}
|
||||
|
||||
if (!residualDepth.isUnconstrained() && residualDepth.getLimit().get() == 0) {
|
||||
throw new IllegalArgumentException("Not enough depth.");
|
||||
}
|
||||
|
||||
boolean cyclic = getRoot().getFingerprint().equals(certification.getTarget().getFingerprint());
|
||||
for (int i = 0; i < edges.size() && !cyclic; i++) {
|
||||
Certification edge = edges.get(i);
|
||||
|
||||
if (edge.getTarget().getFingerprint().equals(certification.getTarget().getFingerprint())) {
|
||||
if (i == edges.size() - 1) {
|
||||
cyclic = edge.getUserId().equals(certification.getUserId());
|
||||
} else {
|
||||
cyclic = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cyclic) {
|
||||
throw new IllegalArgumentException("Adding the certification to the path would create a cycle.");
|
||||
}
|
||||
|
||||
residualDepth = certification.getTrustDepth().min(residualDepth.decrease(1));
|
||||
edges.add(certification);
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.wot.dijkstra.sq;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Paths {
|
||||
|
||||
private final List<Item> paths = new ArrayList<>();
|
||||
|
||||
public List<Item> getPaths() {
|
||||
return new ArrayList<>(paths);
|
||||
}
|
||||
|
||||
public void add(Path path, int amount) {
|
||||
if (amount <= path.getAmount()) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
this.paths.add(new Item(path, amount));
|
||||
}
|
||||
|
||||
public int getAmount() {
|
||||
int sum = 0;
|
||||
for (Item item : paths) {
|
||||
sum += item.amount;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
public static class Item {
|
||||
private final Path path;
|
||||
private final int amount;
|
||||
|
||||
public Item(Path path, int amount) {
|
||||
this.path = path;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public Path getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public int getAmount() {
|
||||
return amount;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.wot.dijkstra.sq;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public final class RegexSet {
|
||||
|
||||
private final Set<String> regexStrings;
|
||||
|
||||
private RegexSet(Set<String> regexStrings) {
|
||||
this.regexStrings = regexStrings;
|
||||
}
|
||||
|
||||
public static RegexSet fromExpressionList(@Nonnull List<String> regexList) {
|
||||
Set<String> regexStringSet = new HashSet<>(regexList);
|
||||
return new RegexSet(regexStringSet);
|
||||
}
|
||||
|
||||
public static RegexSet fromExpression(@Nonnull String regex) {
|
||||
return fromExpressionList(Collections.singletonList(regex));
|
||||
}
|
||||
|
||||
public static RegexSet wildcard() {
|
||||
return fromExpressionList(Collections.emptyList());
|
||||
}
|
||||
|
||||
public boolean matches(String string) {
|
||||
if (regexStrings.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (String regex : regexStrings) {
|
||||
Matcher matcher = Pattern.compile(regex).matcher(string);
|
||||
if (matcher.matches()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,3 +1,7 @@
|
|||
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.wot.dijkstra.sq
|
||||
|
||||
import org.pgpainless.algorithm.RevocationState
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.wot.dijkstra.sq
|
||||
|
||||
import java.util.*
|
||||
|
||||
data class Certification(
|
||||
val issuer: CertSynopsis,
|
||||
val target: CertSynopsis,
|
||||
val userId: String?,
|
||||
val creationTime: Date,
|
||||
val expirationTime: Date?,
|
||||
val exportable: Boolean,
|
||||
val trustAmount: Int,
|
||||
val trustDepth: Depth,
|
||||
val regex: RegexSet
|
||||
) {
|
||||
|
||||
constructor(
|
||||
issuer: CertSynopsis,
|
||||
targetUserId: String?,
|
||||
target: CertSynopsis,
|
||||
creationTime: Date) :
|
||||
this(issuer, target, targetUserId, creationTime, null, true, 120, Depth.limited(0), RegexSet.wildcard())
|
||||
|
||||
override fun toString(): String {
|
||||
val relation = if (userId != null) {
|
||||
"certifies"
|
||||
} else {
|
||||
"delegates to"
|
||||
}
|
||||
val relationTarget = if (userId != null) {
|
||||
"[$userId] ${target.fingerprint}"
|
||||
} else {
|
||||
"$target"
|
||||
}
|
||||
return "$issuer $relation $relationTarget"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.wot.dijkstra.sq
|
||||
|
||||
data class CertificationSet(
|
||||
val issuer: CertSynopsis,
|
||||
val target: CertSynopsis,
|
||||
val certifications: MutableMap<String?, MutableList<Certification>>) {
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun empty(issuer: CertSynopsis, target: CertSynopsis): CertificationSet {
|
||||
return CertificationSet(issuer, target, HashMap())
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun fromCertification(certification: Certification) : CertificationSet {
|
||||
val set = empty(certification.issuer, certification.target)
|
||||
set.add(certification)
|
||||
return set
|
||||
}
|
||||
}
|
||||
|
||||
fun merge(other: CertificationSet) {
|
||||
if (other == this) {
|
||||
return
|
||||
}
|
||||
|
||||
require(issuer.fingerprint == other.issuer.fingerprint) { "Issuer fingerprint mismatch." }
|
||||
require(target.fingerprint == other.target.fingerprint) { "Target fingerprint mismatch." }
|
||||
|
||||
for (userId in other.certifications.keys) {
|
||||
for (certification in other.certifications[userId]!!) {
|
||||
add(certification)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun add(certification : Certification) {
|
||||
require(issuer.fingerprint == certification.issuer.fingerprint) { "Issuer fingerprint mismatch." }
|
||||
require(target.fingerprint == certification.target.fingerprint) { "Target fingerprint mismatch." }
|
||||
|
||||
var certificationsForUserId: MutableList<Certification>? = certifications[certification.userId]
|
||||
if (certificationsForUserId == null) {
|
||||
certificationsForUserId = ArrayList()
|
||||
certifications[certification.userId] = certificationsForUserId
|
||||
}
|
||||
certificationsForUserId.add(certification)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "$certifications"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.wot.dijkstra.sq
|
||||
|
||||
class Depth(val limit: Int?) : Comparable<Depth> {
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun unconstrained() : Depth {
|
||||
return Depth(null)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun limited(limit: Int): Depth {
|
||||
require(limit in 0..255) {
|
||||
"Trust depth MUST be a value between 0 and 255."
|
||||
}
|
||||
return Depth(limit)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun auto(limit: Int): Depth {
|
||||
return if (limit == 255) {
|
||||
unconstrained()
|
||||
} else {
|
||||
limited(limit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun isUnconstrained() : Boolean {
|
||||
return limit == null
|
||||
}
|
||||
|
||||
fun decrease(value : Int) : Depth {
|
||||
return if (isUnconstrained()) {
|
||||
unconstrained()
|
||||
} else {
|
||||
if (limit!! >= value) {
|
||||
limited(limit - value)
|
||||
} else {
|
||||
throw IllegalArgumentException("Depth cannot be decreased.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun min(other: Depth) : Depth {
|
||||
return if (compareTo(other) <= 0) {
|
||||
this
|
||||
} else {
|
||||
other
|
||||
}
|
||||
}
|
||||
|
||||
override fun compareTo(o: Depth): Int {
|
||||
return if (isUnconstrained()) {
|
||||
if (o.isUnconstrained()) {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
}
|
||||
} else {
|
||||
if (o.isUnconstrained()) {
|
||||
-1
|
||||
} else {
|
||||
limit!!.compareTo(o.limit!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString() : String {
|
||||
return if (isUnconstrained()) { "unconstrained" } else { limit!!.toString() }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.wot.dijkstra.sq
|
||||
|
||||
import org.pgpainless.key.OpenPgpFingerprint
|
||||
|
||||
/**
|
||||
* A network consists of nodes, and edges between them.
|
||||
* For the Web of Trust, nodes consist of [CertSynopses][CertSynopsis], while the edges between the nodes are
|
||||
* [CertificationSets][CertificationSet].
|
||||
*
|
||||
* @constructor creates a new network
|
||||
* @param nodes contains a [Map] of [CertSynopsis] keyed by their [OpenPgpFingerprint]
|
||||
* @param edges [Map] keyed by the [fingerprint][OpenPgpFingerprint] of an issuer, whose values are [Lists][List] containing all edges originating from the issuer.
|
||||
* @param reverseEdges [Map] keyed by the [fingerprint][OpenPgpFingerprint] of a target, whose values are [Lists][List] containing all edges pointing to the target
|
||||
* @param referenceTime reference time at which the [Network] was built
|
||||
*/
|
||||
class Network(
|
||||
val nodes: Map<OpenPgpFingerprint, CertSynopsis>,
|
||||
val edges: Map<OpenPgpFingerprint, List<CertificationSet>>,
|
||||
val reverseEdges: Map<OpenPgpFingerprint, List<CertificationSet>>,
|
||||
val referenceTime: ReferenceTime) {
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun empty(referenceTime: ReferenceTime): Network {
|
||||
return Network(HashMap(), HashMap(), HashMap(), referenceTime)
|
||||
}
|
||||
}
|
||||
|
||||
val numberOfEdges: Int
|
||||
get() {
|
||||
return edges.values.sumOf { it.size }
|
||||
}
|
||||
|
||||
val numberOfSignatures: Int
|
||||
get() {
|
||||
return edges.values
|
||||
.flatten()
|
||||
.flatMap { it.certifications.values }
|
||||
.sumOf { it.size }
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
val sb = StringBuilder()
|
||||
sb.append("Network with ${nodes.size} nodes, $numberOfEdges edges:\n")
|
||||
for (issuer in nodes.keys) {
|
||||
val outEdges = edges[issuer] ?: continue
|
||||
for (edge in outEdges) {
|
||||
sb.append(edge)
|
||||
}
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.wot.dijkstra.sq
|
||||
|
||||
import kotlin.math.min
|
||||
|
||||
class Path(
|
||||
val root: CertSynopsis,
|
||||
val edges: MutableList<Certification>,
|
||||
var residualDepth: Depth
|
||||
) {
|
||||
constructor(root: CertSynopsis) : this(
|
||||
root, mutableListOf<Certification>(), Depth.unconstrained())
|
||||
|
||||
val target: CertSynopsis
|
||||
get() {
|
||||
return if (edges.isEmpty()) {
|
||||
root
|
||||
} else {
|
||||
edges[edges.size - 1].target
|
||||
}
|
||||
}
|
||||
|
||||
val certificates: List<CertSynopsis>
|
||||
get() {
|
||||
val certs: MutableList<CertSynopsis> = ArrayList()
|
||||
certs.add(root)
|
||||
for (certification in edges) {
|
||||
certs.add(certification.target)
|
||||
}
|
||||
return certs
|
||||
}
|
||||
|
||||
val length: Int
|
||||
get() = edges.size + 1
|
||||
|
||||
val certifications: List<Certification>
|
||||
get() = ArrayList(edges)
|
||||
|
||||
val amount: Int
|
||||
get() = if (edges.isEmpty()) {
|
||||
120
|
||||
} else {
|
||||
var min = 255
|
||||
for (edge in edges) {
|
||||
min = min(min, edge.trustAmount)
|
||||
}
|
||||
min
|
||||
}
|
||||
|
||||
fun append(certification: Certification) {
|
||||
require(target.fingerprint == certification.issuer.fingerprint) {
|
||||
"Cannot append certification to path: Path's tail is not issuer of the certification."
|
||||
}
|
||||
require(residualDepth.isUnconstrained() || residualDepth.limit!! > 0) {
|
||||
"Not enough depth."
|
||||
}
|
||||
|
||||
var cyclic = root.fingerprint == certification.target.fingerprint
|
||||
for (i in 0..edges.size) {
|
||||
val edge = edges[i]
|
||||
if (cyclic) {
|
||||
break
|
||||
}
|
||||
if (edge.target.fingerprint == certification.target.fingerprint) {
|
||||
cyclic = if (i == edges.size - 1) {
|
||||
edge.userId == certification.userId
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
require(!cyclic) { "Adding the certification to the path would create a cycle." }
|
||||
|
||||
residualDepth = certification.trustDepth.min(residualDepth.decrease(1))
|
||||
edges.add(certification)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.wot.dijkstra.sq
|
||||
|
||||
class Paths(val paths: MutableList<Item>) {
|
||||
|
||||
fun add(path: Path, amount: Int) {
|
||||
require(amount <= path.amount) {
|
||||
"Amount too small. TODO: Better error message"
|
||||
}
|
||||
paths.add(Item(path, amount))
|
||||
}
|
||||
|
||||
val amount: Int
|
||||
get() {
|
||||
return paths.sumOf { it.amount }
|
||||
}
|
||||
|
||||
data class Item(val path: Path, val amount: Int) {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.wot.dijkstra.sq
|
||||
|
||||
import java.util.regex.Pattern
|
||||
|
||||
data class RegexSet(val regexStrings: Set<String>) {
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun fromExpressionList(regexList: List<String>): RegexSet {
|
||||
return RegexSet(regexList.toSet())
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun fromExpression(regex: String): RegexSet {
|
||||
return fromExpressionList(listOf(regex))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun wildcard(): RegexSet {
|
||||
return fromExpressionList(listOf())
|
||||
}
|
||||
}
|
||||
|
||||
fun matches(string: String): Boolean {
|
||||
if (regexStrings.isEmpty()) {
|
||||
return true
|
||||
}
|
||||
|
||||
for (regex in regexStrings) {
|
||||
val matcher = Pattern.compile(regex).matcher(string)
|
||||
if (matcher.matches()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -8,6 +8,8 @@ import org.junit.jupiter.api.Test;
|
|||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
|
@ -17,15 +19,15 @@ public class DepthTest {
|
|||
public void testUnlimitedItem() {
|
||||
Depth depth = Depth.unconstrained();
|
||||
assertTrue(depth.isUnconstrained());
|
||||
assertFalse(depth.getLimit().isPresent());
|
||||
assertNull(depth.getLimit());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLimitedItem() {
|
||||
Depth limited = Depth.limited(2);
|
||||
assertFalse(limited.isUnconstrained());
|
||||
assertTrue(limited.getLimit().isPresent());
|
||||
assertEquals(2, limited.getLimit().get());
|
||||
assertNotNull(limited.getLimit());
|
||||
assertEquals(2, limited.getLimit());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -40,7 +42,7 @@ public class DepthTest {
|
|||
Depth limited = Depth.limited(1);
|
||||
Depth decreased = limited.decrease(1);
|
||||
assertFalse(decreased.isUnconstrained());
|
||||
assertEquals(0, decreased.getLimit().get());
|
||||
assertEquals(0, decreased.getLimit());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -76,7 +78,7 @@ public class DepthTest {
|
|||
public void testAutoLimited() {
|
||||
Depth depth = Depth.auto(120);
|
||||
assertFalse(depth.isUnconstrained());
|
||||
assertEquals(120, depth.getLimit().get());
|
||||
assertEquals(120, depth.getLimit());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in a new issue