mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-11-05 03:55:58 +01:00
WIP: Identify command
This commit is contained in:
parent
fbdcae3c81
commit
c98d6c4708
6 changed files with 94 additions and 17 deletions
|
@ -15,12 +15,15 @@ import org.pgpainless.wot.network.Fingerprint
|
|||
import org.pgpainless.wot.network.ReferenceTime
|
||||
import org.pgpainless.wot.network.Root
|
||||
import org.pgpainless.wot.network.Roots
|
||||
import pgp.cert_d.PGPCertificateDirectory
|
||||
import pgp.cert_d.PGPCertificateStoreAdapter
|
||||
import pgp.cert_d.SpecialNames
|
||||
import pgp.cert_d.subkey_lookup.InMemorySubkeyLookupFactory
|
||||
import pgp.certificate_store.PGPCertificateStore
|
||||
import picocli.CommandLine
|
||||
import picocli.CommandLine.*
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.concurrent.Callable
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
|
@ -108,22 +111,28 @@ class WotCLI: Callable<Int> {
|
|||
|
||||
private val trustRoots: Roots
|
||||
get() {
|
||||
val trustRootFingerprints = if (mCertificateSource.gpg || gpgOwnertrust) {
|
||||
readGpgOwnertrust().plus(mTrustRoot.map { Fingerprint(it) })
|
||||
} else {
|
||||
mTrustRoot.map { Fingerprint(it) }
|
||||
var trustRootFingerprints = mTrustRoot.map { Fingerprint(it) }.map { Root(it) }
|
||||
if (mCertificateSource.gpg || gpgOwnertrust) {
|
||||
trustRootFingerprints = trustRootFingerprints.plus(readGpgOwnertrust())
|
||||
}
|
||||
|
||||
return Roots(trustRootFingerprints.map { Root(it) })
|
||||
if (mCertificateSource.pgpCertD != null) {
|
||||
try {
|
||||
val rootCert = certificateStore.getCertificate(SpecialNames.TRUST_ROOT)
|
||||
trustRootFingerprints = trustRootFingerprints.plus(Root(Fingerprint(rootCert.fingerprint), Int.MAX_VALUE))
|
||||
} catch (e: NoSuchElementException) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return Roots(trustRootFingerprints)
|
||||
}
|
||||
|
||||
private val amount: Int
|
||||
get() = when {
|
||||
mTrustAmount.amount != null -> mTrustAmount.amount!! // --amount=XY
|
||||
mTrustAmount.amount != null -> mTrustAmount.amount!! // --amount=XY
|
||||
mTrustAmount.partial -> 40 // --partial
|
||||
mTrustAmount.full -> 120 // --full
|
||||
mTrustAmount.double -> 240 // --double
|
||||
else -> if (certificationNetwork) 1200 else 120 // default 120, if --certification-network -> 1200
|
||||
else -> if (certificationNetwork) 1200 else 120 // default 120, if --certification-network -> 1200
|
||||
}
|
||||
|
||||
private val certificateStore: PGPCertificateStore
|
||||
|
@ -149,15 +158,31 @@ class WotCLI: Callable<Int> {
|
|||
return PGPCertificateStoreAdapter(certD)
|
||||
}
|
||||
|
||||
fun readGpgOwnertrust(): List<Fingerprint> = Runtime.getRuntime()
|
||||
fun readGpgOwnertrust(): List<Root> = Runtime.getRuntime()
|
||||
.exec("/usr/bin/gpg --export-ownertrust")
|
||||
.inputStream
|
||||
.bufferedReader()
|
||||
.readLines()
|
||||
.asSequence()
|
||||
.filterNot { it.startsWith("#") }
|
||||
.filterNot { it.isBlank() }
|
||||
.map { it.substring(0, it.indexOf(':')) }
|
||||
.map { Fingerprint(it) }
|
||||
.map {
|
||||
Fingerprint(it.substring(0, it.indexOf(':'))) to it.elementAt(it.indexOf(':') + 1) }
|
||||
.map {
|
||||
it.first to when (it.second.digitToInt()) {
|
||||
2 -> null // unknown
|
||||
3 -> 0 // not trust
|
||||
4 -> 40 // marginally trusted
|
||||
5 -> 120 // fully trusted
|
||||
6 -> Int.MAX_VALUE // ultimately trusted
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
.filterNot { it.second == null }
|
||||
.map {
|
||||
Root(it.first, it.second!!)
|
||||
}
|
||||
.toList()
|
||||
|
||||
/**
|
||||
* Execute the command.
|
||||
|
@ -196,6 +221,9 @@ class WotCLI: Callable<Int> {
|
|||
CommandLine(WotCLI()).execute(*args)
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
val dateFormat: SimpleDateFormat = SimpleDateFormat("yyyy-MM-dd")
|
||||
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
|
|
|
@ -45,7 +45,7 @@ class AuthenticateCmd: Callable<Int> {
|
|||
@CommandLine.Option(names = ["--email"], description = ["Consider all user-IDs that contain the given email address."])
|
||||
var email = false
|
||||
|
||||
private val dateFormat: SimpleDateFormat = SimpleDateFormat("yyyy-MM-dd")
|
||||
|
||||
|
||||
/**
|
||||
* Execute the command.
|
||||
|
@ -83,7 +83,7 @@ class AuthenticateCmd: Callable<Int> {
|
|||
sb.appendLine(" ├ ${certification.issuer.fingerprint}${issuerUserId}")
|
||||
}
|
||||
}
|
||||
sb.appendLine(" │ certified the following binding on ${dateFormat.format(certification.creationTime)}")
|
||||
sb.appendLine(" │ certified the following binding on ${WotCLI.dateFormat.format(certification.creationTime)}")
|
||||
}
|
||||
sb.appendLine(" └ ${result.fingerprint} \"${result.userId}\"")
|
||||
}
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
|
||||
package org.pgpainless.wot.cli.subcommands
|
||||
|
||||
import org.pgpainless.wot.api.IdentifyAPI
|
||||
import org.pgpainless.wot.cli.WotCLI
|
||||
import org.pgpainless.wot.network.Fingerprint
|
||||
import picocli.CommandLine
|
||||
import picocli.CommandLine.Command
|
||||
import picocli.CommandLine.Parameters
|
||||
|
@ -26,7 +28,36 @@ class IdentifyCmd: Callable<Int> {
|
|||
*/
|
||||
override fun call(): Int {
|
||||
val api = parent.api
|
||||
TODO("Not yet implemented")
|
||||
val result = api.identify(IdentifyAPI.Arguments(Fingerprint(fingerprint)))
|
||||
println(formatResult(result))
|
||||
return exitCode(result)
|
||||
}
|
||||
|
||||
fun formatResult(result: IdentifyAPI.Result): String {
|
||||
if (result.target == null || result.paths.isEmpty()) {
|
||||
return "No paths found."
|
||||
}
|
||||
|
||||
return buildString {
|
||||
result.paths.keys.forEach { userId ->
|
||||
val target = result.target!!
|
||||
appendLine("[✓] ${target.fingerprint} $userId: fully authenticated: (${result.percentage(userId)}%)")
|
||||
result.paths[userId]!!.paths.forEach {path ->
|
||||
val root = path.root
|
||||
val userIdString = if (root.userIds.isEmpty()) "" else " (${root.userIds.keys.first()})"
|
||||
appendLine(" ◯ ${root.fingerprint}$userIdString")
|
||||
path.certifications.forEachIndexed { index, edge ->
|
||||
appendLine(" │ certified the following binding on ${WotCLI.dateFormat.format(edge.creationTime)}")
|
||||
append(" ").append(if (index == path.certifications.lastIndex) "└" else "├")
|
||||
.appendLine(" ${edge.target.fingerprint} \"${edge.userId}\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun exitCode(result: IdentifyAPI.Result): Int {
|
||||
return if(result.paths.isEmpty()) -1 else 0
|
||||
}
|
||||
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
package org.pgpainless.wot.api
|
||||
|
||||
import org.pgpainless.wot.network.Fingerprint
|
||||
import org.pgpainless.wot.network.Node
|
||||
import org.pgpainless.wot.query.Paths
|
||||
|
||||
interface IdentifyAPI {
|
||||
|
@ -13,5 +14,12 @@ interface IdentifyAPI {
|
|||
|
||||
data class Arguments(val fingerprint: Fingerprint)
|
||||
|
||||
data class Result(val paths: Paths)
|
||||
data class Result(val paths: Map<String, Paths>, val target: Node?, val targetAmount: Int) {
|
||||
fun percentage(userId: String): Int? {
|
||||
if (paths[userId] == null) {
|
||||
return null
|
||||
}
|
||||
return paths[userId]!!.amount * 100 / targetAmount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ import org.pgpainless.wot.network.Fingerprint
|
|||
import org.pgpainless.wot.network.Network
|
||||
import org.pgpainless.wot.network.ReferenceTime
|
||||
import org.pgpainless.wot.network.Roots
|
||||
import org.pgpainless.wot.query.Path
|
||||
import org.pgpainless.wot.query.Paths
|
||||
|
||||
/**
|
||||
* Web of Trust API, offering different operations.
|
||||
|
@ -48,7 +50,16 @@ class WoTAPI(
|
|||
}
|
||||
|
||||
override fun identify(arguments: IdentifyAPI.Arguments): IdentifyAPI.Result {
|
||||
TODO("Not yet implemented")
|
||||
val cert = network.nodes[arguments.fingerprint] ?: return IdentifyAPI.Result(mutableMapOf(), null, trustAmount)
|
||||
val allPaths = mutableMapOf<String, Paths>()
|
||||
cert.userIds.keys.toList().forEach {
|
||||
val query = Query(network, trustRoots, certificationNetwork)
|
||||
val paths = query.authenticate(arguments.fingerprint, it, trustAmount)
|
||||
if (paths.amount != 0) {
|
||||
allPaths[it] = paths
|
||||
}
|
||||
}
|
||||
return IdentifyAPI.Result(allPaths, cert, trustAmount)
|
||||
}
|
||||
|
||||
override fun list(): ListAPI.Result {
|
||||
|
|
|
@ -125,7 +125,6 @@ class DepthTest {
|
|||
assertThrows<IllegalArgumentException> { limited(-1) }
|
||||
assertThrows<IllegalArgumentException> { limited(256) }
|
||||
assertThrows<IllegalArgumentException> { auto(-1) }
|
||||
assertThrows<IllegalArgumentException> { auto(256) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in a new issue