mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-09-26 17:49:34 +02:00
Proper formatting for outputs
This commit is contained in:
parent
e2be3c002f
commit
111c84db97
|
@ -9,15 +9,14 @@ import org.pgpainless.certificate_store.PGPainlessCertD
|
||||||
import org.pgpainless.util.DateUtil
|
import org.pgpainless.util.DateUtil
|
||||||
import org.pgpainless.wot.KeyRingCertificateStore
|
import org.pgpainless.wot.KeyRingCertificateStore
|
||||||
import org.pgpainless.wot.WebOfTrust
|
import org.pgpainless.wot.WebOfTrust
|
||||||
import org.pgpainless.wot.api.Binding
|
import org.pgpainless.wot.cli.format.Formatter
|
||||||
import org.pgpainless.wot.api.WoTAPI
|
import org.pgpainless.wot.api.WoTAPI
|
||||||
|
import org.pgpainless.wot.cli.format.HumanReadableFormatter
|
||||||
import org.pgpainless.wot.cli.subcommands.*
|
import org.pgpainless.wot.cli.subcommands.*
|
||||||
import org.pgpainless.wot.network.Fingerprint
|
import org.pgpainless.wot.network.Fingerprint
|
||||||
import org.pgpainless.wot.network.ReferenceTime
|
import org.pgpainless.wot.network.ReferenceTime
|
||||||
import org.pgpainless.wot.network.Root
|
import org.pgpainless.wot.network.Root
|
||||||
import org.pgpainless.wot.network.Roots
|
import org.pgpainless.wot.network.Roots
|
||||||
import org.pgpainless.wot.query.Path
|
|
||||||
import pgp.cert_d.PGPCertificateDirectory
|
|
||||||
import pgp.cert_d.PGPCertificateStoreAdapter
|
import pgp.cert_d.PGPCertificateStoreAdapter
|
||||||
import pgp.cert_d.SpecialNames
|
import pgp.cert_d.SpecialNames
|
||||||
import pgp.cert_d.subkey_lookup.InMemorySubkeyLookupFactory
|
import pgp.cert_d.subkey_lookup.InMemorySubkeyLookupFactory
|
||||||
|
@ -160,6 +159,8 @@ class WotCLI: Callable<Int> {
|
||||||
return PGPCertificateStoreAdapter(certD)
|
return PGPCertificateStoreAdapter(certD)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val formatter: Formatter = HumanReadableFormatter()
|
||||||
|
|
||||||
fun readGpgOwnertrust(): List<Root> = Runtime.getRuntime()
|
fun readGpgOwnertrust(): List<Root> = Runtime.getRuntime()
|
||||||
.exec("/usr/bin/gpg --export-ownertrust")
|
.exec("/usr/bin/gpg --export-ownertrust")
|
||||||
.inputStream
|
.inputStream
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package org.pgpainless.wot.cli.format
|
||||||
|
|
||||||
|
import org.pgpainless.wot.api.*
|
||||||
|
|
||||||
|
interface Formatter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a binding.
|
||||||
|
* @param binding binding to format
|
||||||
|
* @param amountMin minimum trust amount to accept the binding
|
||||||
|
* @param amountReference reference value to compare the amount against to calculate percentage
|
||||||
|
*/
|
||||||
|
fun format(binding: Binding, amountMin: Int = 120, amountReference: Int = 120): String
|
||||||
|
|
||||||
|
fun format(authenticateResult: AuthenticateAPI.Result): String {
|
||||||
|
return buildString {
|
||||||
|
append(format(authenticateResult.binding, authenticateResult.targetAmount))
|
||||||
|
if (!authenticateResult.acceptable) {
|
||||||
|
appendLine()
|
||||||
|
append("Could not authenticate any paths.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun format(identifyResult: IdentifyAPI.Result): String {
|
||||||
|
return buildString {
|
||||||
|
identifyResult.bindings.forEach {
|
||||||
|
appendLine(format(it, identifyResult.targetAmount))
|
||||||
|
}
|
||||||
|
if (!identifyResult.acceptable) {
|
||||||
|
appendLine("Could not authenticate any paths.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun format(listResult: ListAPI.Result): String {
|
||||||
|
return buildString {
|
||||||
|
listResult.bindings.forEach {
|
||||||
|
appendLine(format(it, listResult.targetAmount))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun format(lookupResult: LookupAPI.Result): String {
|
||||||
|
return buildString {
|
||||||
|
lookupResult.bindings.forEach {
|
||||||
|
appendLine(format(it, lookupResult.targetAmount))
|
||||||
|
}
|
||||||
|
if (!lookupResult.acceptable) {
|
||||||
|
appendLine("Could not authenticate any paths.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package org.pgpainless.wot.cli.format
|
||||||
|
|
||||||
|
import org.pgpainless.wot.api.Binding
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
|
||||||
|
class HumanReadableFormatter: Formatter {
|
||||||
|
|
||||||
|
private val dateFormat: SimpleDateFormat = SimpleDateFormat("yyyy-MM-dd")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a single binding
|
||||||
|
*/
|
||||||
|
override fun format(binding: Binding, amountMin: Int, amountReference: Int): String {
|
||||||
|
val percentage = binding.percentage(amountReference)
|
||||||
|
val authLevel = when(binding.paths.amount) {
|
||||||
|
in 0..39 -> "not authenticated"
|
||||||
|
in 40..119 -> "partially authenticated"
|
||||||
|
in 120 .. 239 -> "fully authenticated"
|
||||||
|
else -> {if (percentage < 0) "not authenticated" else "doubly authenticated"}
|
||||||
|
}
|
||||||
|
val checkmark = if(binding.paths.amount >= amountMin) "[✓] " else "[ ] "
|
||||||
|
val pathList = binding.paths.paths
|
||||||
|
val singlePath = pathList.size == 1
|
||||||
|
val indent = " ".repeat(if (singlePath) 2 else 4)
|
||||||
|
|
||||||
|
return buildString {
|
||||||
|
// [✓] 7F9116FEA90A5983936C7CFAA027DB2F3E1E118A Paul Schaub <vanitasvitae@fsfe.org>: fully authenticated (100%)
|
||||||
|
append(checkmark); appendLine("${binding.fingerprint} ${binding.userId}: $authLevel (${percentage}%)")
|
||||||
|
for ((pIndex, path) in pathList.withIndex()) {
|
||||||
|
if (!singlePath) {
|
||||||
|
appendLine(" Path #${pIndex + 1} of ${pathList.size}, trust amount ${path.amount}:")
|
||||||
|
}
|
||||||
|
val originUserId = if (path.root.userIds.isEmpty())
|
||||||
|
""
|
||||||
|
else if (path.root.fingerprint == path.target.fingerprint)
|
||||||
|
" \"${path.root.userIds.keys.first()}\""
|
||||||
|
else
|
||||||
|
" (\"${path.root.userIds.keys.first()}\")"
|
||||||
|
append(indent); appendLine("◯ ${path.root.fingerprint}$originUserId")
|
||||||
|
for ((eIndex, edge) in path.certifications.withIndex()) {
|
||||||
|
val targetUserId = if (edge.userId == null) "" else " \"${edge.userId}\""
|
||||||
|
append(indent); appendLine("│ certified the following binding on ${dateFormat.format(edge.creationTime)}")
|
||||||
|
append(indent); append(if (eIndex != path.certifications.lastIndex) "├ " else "└ ")
|
||||||
|
appendLine("${edge.target.fingerprint}$targetUserId")
|
||||||
|
}
|
||||||
|
if (pIndex != pathList.lastIndex) {
|
||||||
|
appendLine()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,8 +45,6 @@ class AuthenticateCmd: Callable<Int> {
|
||||||
@CommandLine.Option(names = ["--email"], description = ["Consider all user-IDs that contain the given email address."])
|
@CommandLine.Option(names = ["--email"], description = ["Consider all user-IDs that contain the given email address."])
|
||||||
var email = false
|
var email = false
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the command.
|
* Execute the command.
|
||||||
* @return exit code
|
* @return exit code
|
||||||
|
@ -54,20 +52,8 @@ class AuthenticateCmd: Callable<Int> {
|
||||||
override fun call(): Int {
|
override fun call(): Int {
|
||||||
val result = parent.api.authenticate(AuthenticateAPI.Arguments(
|
val result = parent.api.authenticate(AuthenticateAPI.Arguments(
|
||||||
Fingerprint(fingerprint), userId, email))
|
Fingerprint(fingerprint), userId, email))
|
||||||
formatResult(result)
|
|
||||||
if (result.percentage < 100) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
println(parent.formatter.format(result))
|
||||||
* Format the [AuthenticateAPI.Result] as a [String] which can be printed to standard out.
|
return if (result.acceptable) 0 else 1
|
||||||
*/
|
|
||||||
internal fun formatResult(result: AuthenticateAPI.Result) {
|
|
||||||
if (result.percentage < 100) {
|
|
||||||
println("No paths found.")
|
|
||||||
}
|
|
||||||
println(result.binding.toConsoleOut(result.targetAmount, WotCLI.dateFormat))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -28,26 +28,9 @@ class IdentifyCmd: Callable<Int> {
|
||||||
* @return exit code
|
* @return exit code
|
||||||
*/
|
*/
|
||||||
override fun call(): Int {
|
override fun call(): Int {
|
||||||
val api = parent.api
|
val result = parent.api.identify(IdentifyAPI.Arguments(Fingerprint(fingerprint)))
|
||||||
val result = api.identify(IdentifyAPI.Arguments(Fingerprint(fingerprint)))
|
|
||||||
println(formatResult(result, api.trustAmount, WotCLI.dateFormat))
|
|
||||||
return exitCode(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun formatResult(result: IdentifyAPI.Result, targetAmount: Int, dateFormat: SimpleDateFormat): String {
|
print(parent.formatter.format(result))
|
||||||
if (result.bindings.isEmpty()) {
|
return if (result.acceptable) 0 else 1
|
||||||
return "No paths found."
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildString {
|
|
||||||
result.bindings.forEach {
|
|
||||||
appendLine(it.toConsoleOut(targetAmount, dateFormat))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun exitCode(result: IdentifyAPI.Result): Int {
|
|
||||||
return if(result.bindings.isEmpty()) -1 else 0
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -21,13 +21,9 @@ class ListCmd: Callable<Int> {
|
||||||
* @return exit code
|
* @return exit code
|
||||||
*/
|
*/
|
||||||
override fun call(): Int {
|
override fun call(): Int {
|
||||||
val api = parent.api
|
val result = parent.api.list()
|
||||||
val result = api.list()
|
|
||||||
println(buildString {
|
println(parent.formatter.format(result))
|
||||||
result.bindings.forEach {
|
|
||||||
appendLine(it.toConsoleOut(api.trustAmount, WotCLI.dateFormat))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -27,11 +27,9 @@ class LookupCmd: Callable<Int> {
|
||||||
* @return exit code
|
* @return exit code
|
||||||
*/
|
*/
|
||||||
override fun call(): Int {
|
override fun call(): Int {
|
||||||
val api = parent.api
|
val result = parent.api.lookup(LookupAPI.Arguments(userId, email))
|
||||||
val result = api.lookup(LookupAPI.Arguments(userId, email))
|
|
||||||
result.bindings.forEach {
|
print(parent.formatter.format(result))
|
||||||
println(it.toConsoleOut(api.trustAmount, WotCLI.dateFormat))
|
return if (result.acceptable) 0 else 1
|
||||||
}
|
|
||||||
return if (result.bindings.isEmpty()) -1 else 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,13 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package org.pgpainless.wot.cli.subcommands
|
package org.pgpainless.wot.cli.subcommands
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.pgpainless.wot.api.AuthenticateAPI
|
import org.pgpainless.wot.api.AuthenticateAPI
|
||||||
|
import org.pgpainless.wot.api.Binding
|
||||||
|
import org.pgpainless.wot.cli.format.HumanReadableFormatter
|
||||||
import org.pgpainless.wot.network.*
|
import org.pgpainless.wot.network.*
|
||||||
import org.pgpainless.wot.query.Path
|
import org.pgpainless.wot.query.Path
|
||||||
import org.pgpainless.wot.query.Paths
|
import org.pgpainless.wot.query.Paths
|
||||||
|
@ -42,19 +48,18 @@ class AuthenticateCmdTest {
|
||||||
Depth.limited(0),
|
Depth.limited(0),
|
||||||
RegexSet.wildcard())
|
RegexSet.wildcard())
|
||||||
paths.add(Path(neal, mutableListOf(edgeComponent), Depth.auto(0)), 120)
|
paths.add(Path(neal, mutableListOf(edgeComponent), Depth.auto(0)), 120)
|
||||||
val testResult = AuthenticateAPI.Result(
|
val testResult = AuthenticateAPI.Result(Binding(
|
||||||
Fingerprint("CBCD8F030588653EEDD7E2659B7DD433F254904A"),
|
Fingerprint("CBCD8F030588653EEDD7E2659B7DD433F254904A"),
|
||||||
"Justus Winter <justus@sequoia-pgp.org>",
|
"Justus Winter <justus@sequoia-pgp.org>",
|
||||||
120,
|
paths),
|
||||||
paths)
|
120, )
|
||||||
|
|
||||||
val formatted = cmd.formatResult(testResult)
|
val formatted = HumanReadableFormatter().format(testResult)
|
||||||
assertEquals(buildString {
|
assertEquals(buildString {
|
||||||
append("[✓] CBCD8F030588653EEDD7E2659B7DD433F254904A Justus Winter <justus@sequoia-pgp.org>: fully authenticated (100%)\n")
|
appendLine("[✓] CBCD8F030588653EEDD7E2659B7DD433F254904A Justus Winter <justus@sequoia-pgp.org>: fully authenticated (100%)")
|
||||||
append(" Path #1 of 1, trust amount 120:\n")
|
appendLine(" ◯ F7173B3C7C685CD9ECC4191B74E445BA0E15C957 (\"Neal H. Walfield (Code Signing Key) <neal@pep.foundation>\")")
|
||||||
append(" ◯ F7173B3C7C685CD9ECC4191B74E445BA0E15C957 (\"Neal H. Walfield (Code Signing Key) <neal@pep.foundation>\")\n")
|
appendLine(" │ certified the following binding on 2022-02-04")
|
||||||
append(" │ certified the following binding on 2022-02-04\n")
|
appendLine(" └ CBCD8F030588653EEDD7E2659B7DD433F254904A \"Justus Winter <justus@sequoia-pgp.org>\"")
|
||||||
append(" └ CBCD8F030588653EEDD7E2659B7DD433F254904A \"Justus Winter <justus@sequoia-pgp.org>\"\n")
|
|
||||||
}, formatted)
|
}, formatted)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -115,7 +115,7 @@ class WebOfTrust(private val certificateStore: PGPCertificateStore) {
|
||||||
val expirationDate: Date? = try {
|
val expirationDate: Date? = try {
|
||||||
cert.getExpirationDateForUse(KeyFlag.CERTIFY_OTHER)
|
cert.getExpirationDateForUse(KeyFlag.CERTIFY_OTHER)
|
||||||
} catch (e: NoSuchElementException) {
|
} catch (e: NoSuchElementException) {
|
||||||
LOGGER.warn("Could not deduce expiration time of ${cert.fingerprint}. " +
|
LOGGER.debug("Could not deduce expiration time of ${cert.fingerprint}. " +
|
||||||
"Possibly hard revoked cert or illegal algorithms? Skip certificate.");
|
"Possibly hard revoked cert or illegal algorithms? Skip certificate.");
|
||||||
// Some keys are malformed and have no KeyFlags
|
// Some keys are malformed and have no KeyFlags
|
||||||
// TODO: We also end up here for expired keys unfortunately
|
// TODO: We also end up here for expired keys unfortunately
|
||||||
|
@ -207,7 +207,7 @@ class WebOfTrust(private val certificateStore: PGPCertificateStore) {
|
||||||
return // we're done
|
return // we're done
|
||||||
} catch (e: SignatureValidationException) {
|
} catch (e: SignatureValidationException) {
|
||||||
val targetFingerprint = OpenPgpFingerprint.of(targetPrimaryKey)
|
val targetFingerprint = OpenPgpFingerprint.of(targetPrimaryKey)
|
||||||
LOGGER.warn("Cannot verify signature by $issuerFingerprint" +
|
LOGGER.debug("Cannot verify signature by $issuerFingerprint" +
|
||||||
" on cert of $targetFingerprint", e)
|
" on cert of $targetFingerprint", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -250,7 +250,7 @@ class WebOfTrust(private val certificateStore: PGPCertificateStore) {
|
||||||
networkBuilder.addEdge(fromCertification(issuer, target, userId, certification))
|
networkBuilder.addEdge(fromCertification(issuer, target, userId, certification))
|
||||||
return // we're done
|
return // we're done
|
||||||
} catch (e: SignatureValidationException) {
|
} catch (e: SignatureValidationException) {
|
||||||
LOGGER.warn("Cannot verify signature for '$userId' by $issuerFingerprint" +
|
LOGGER.debug("Cannot verify signature for '$userId' by $issuerFingerprint" +
|
||||||
" on cert of ${target.fingerprint}", e)
|
" on cert of ${target.fingerprint}", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,9 @@ interface AuthenticateAPI {
|
||||||
data class Result(val binding: Binding, val targetAmount: Int) {
|
data class Result(val binding: Binding, val targetAmount: Int) {
|
||||||
val percentage: Int
|
val percentage: Int
|
||||||
get() = binding.percentage(targetAmount)
|
get() = binding.percentage(targetAmount)
|
||||||
|
|
||||||
|
val acceptable: Boolean
|
||||||
|
get() = binding.paths.amount >= targetAmount
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,34 +16,4 @@ data class Binding(val fingerprint: Fingerprint, val userId: String, val paths:
|
||||||
fun percentage(targetAmount: Int): Int {
|
fun percentage(targetAmount: Int): Int {
|
||||||
return paths.amount * 100 / targetAmount
|
return paths.amount * 100 / targetAmount
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toConsoleOut(targetAmount: Int, dateFormat: SimpleDateFormat): String {
|
|
||||||
return buildString {
|
|
||||||
val percentage = percentage(targetAmount)
|
|
||||||
val authLevel = when (paths.amount) {
|
|
||||||
in 0..39 -> "not authenticated"
|
|
||||||
in 40..119 -> "partially authenticated"
|
|
||||||
in 120 .. 239 -> "fully authenticated"
|
|
||||||
else -> {if (percentage < 0) "not authenticated" else "doubly authenticated"}
|
|
||||||
}
|
|
||||||
append(if (percentage >= 100) "[✓] " else "[ ] ")
|
|
||||||
appendLine("$fingerprint $userId: $authLevel (${percentage(targetAmount)}%)")
|
|
||||||
for ((pIndex, path: Path) in paths.paths.withIndex()) {
|
|
||||||
appendLine(" Path #${pIndex + 1} of ${paths.paths.size}, trust amount ${path.amount}:")
|
|
||||||
for ((cIndex, certification) in path.certifications.withIndex()) {
|
|
||||||
val issuerUserId = certification.issuer.userIds.keys.firstOrNull()?.let { " (\"$it\")" } ?: ""
|
|
||||||
when (cIndex) {
|
|
||||||
0 -> {
|
|
||||||
appendLine(" ◯ ${certification.issuer.fingerprint}$issuerUserId")
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
appendLine(" ├ ${certification.issuer.fingerprint}$issuerUserId")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
appendLine(" │ certified the following binding on ${dateFormat.format(certification.creationTime)}")
|
|
||||||
}
|
|
||||||
appendLine(" └ $fingerprint \"$userId\"")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -14,5 +14,10 @@ interface IdentifyAPI {
|
||||||
|
|
||||||
data class Arguments(val fingerprint: Fingerprint)
|
data class Arguments(val fingerprint: Fingerprint)
|
||||||
|
|
||||||
data class Result(val bindings: List<Binding>, val targetAmount: Int)
|
data class Result(val bindings: List<Binding>, val targetAmount: Int) {
|
||||||
|
val acceptable: Boolean
|
||||||
|
get() = bindings.any {
|
||||||
|
it.paths.amount >= targetAmount
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,5 +8,5 @@ interface ListAPI {
|
||||||
|
|
||||||
fun list(): Result
|
fun list(): Result
|
||||||
|
|
||||||
data class Result(val bindings: List<Binding>)
|
data class Result(val bindings: List<Binding>, val targetAmount: Int)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,5 +10,10 @@ interface LookupAPI {
|
||||||
|
|
||||||
data class Arguments(val userId: String, val email: Boolean = false)
|
data class Arguments(val userId: String, val email: Boolean = false)
|
||||||
|
|
||||||
data class Result(val bindings: List<Binding>)
|
data class Result(val bindings: List<Binding>, val targetAmount: Int) {
|
||||||
|
val acceptable: Boolean
|
||||||
|
get() = bindings.any {
|
||||||
|
it.paths.amount >= targetAmount
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,15 +64,13 @@ class WoTAPI(
|
||||||
network.nodes.forEach {
|
network.nodes.forEach {
|
||||||
bindings.addAll(identify(IdentifyAPI.Arguments(it.key)).bindings)
|
bindings.addAll(identify(IdentifyAPI.Arguments(it.key)).bindings)
|
||||||
}
|
}
|
||||||
return ListAPI.Result(bindings)
|
return ListAPI.Result(bindings, trustAmount)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun lookup(arguments: LookupAPI.Arguments): LookupAPI.Result {
|
override fun lookup(arguments: LookupAPI.Arguments): LookupAPI.Result {
|
||||||
val userId = arguments.userId
|
val userId = arguments.userId
|
||||||
val email = arguments.email
|
val email = arguments.email
|
||||||
|
|
||||||
println("Looking up $userId email=$email")
|
|
||||||
|
|
||||||
val candidates = network.nodes.values.mapNotNull { node ->
|
val candidates = network.nodes.values.mapNotNull { node ->
|
||||||
val matches = node.mapToMatchingUserIds(userId, email)
|
val matches = node.mapToMatchingUserIds(userId, email)
|
||||||
if (matches.isEmpty()) {
|
if (matches.isEmpty()) {
|
||||||
|
@ -82,11 +80,6 @@ class WoTAPI(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println("found ${candidates.size} candidates:")
|
|
||||||
candidates.joinToString {
|
|
||||||
"${it.first.fingerprint} ${it.second.joinToString { u -> u }}"
|
|
||||||
}
|
|
||||||
|
|
||||||
val results = mutableListOf<Binding>()
|
val results = mutableListOf<Binding>()
|
||||||
candidates.forEach {
|
candidates.forEach {
|
||||||
val node = it.first
|
val node = it.first
|
||||||
|
@ -101,7 +94,7 @@ class WoTAPI(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return LookupAPI.Result(results)
|
return LookupAPI.Result(results, trustAmount)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun path(arguments: PathAPI.Arguments): PathAPI.Result {
|
override fun path(arguments: PathAPI.Arguments): PathAPI.Result {
|
||||||
|
|
Loading…
Reference in a new issue