mirror of
https://github.com/corda/corda.git
synced 2025-06-17 22:58:19 +00:00
Support fuzzy matching for identities.
Matching can be done with case insensitive substrings in the identity service, RPC and shell. In future cleverer matching should be possible, e.g. using Lucene or RDBMS free text search features.
This commit is contained in:
@ -8,6 +8,7 @@ import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.FlowInitiator
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.StartableByRPC
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.messaging.*
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.services.NetworkMapCache
|
||||
@ -174,7 +175,8 @@ class CordaRPCOpsImpl(
|
||||
@Suppress("DEPRECATION")
|
||||
@Deprecated("Use partyFromX500Name instead")
|
||||
override fun partyFromName(name: String) = services.identityService.partyFromName(name)
|
||||
override fun partyFromX500Name(x500Name: X500Name)= services.identityService.partyFromX500Name(x500Name)
|
||||
override fun partyFromX500Name(x500Name: X500Name) = services.identityService.partyFromX500Name(x500Name)
|
||||
override fun partiesFromName(query: String, exactMatch: Boolean): Set<Party> = services.identityService.partiesFromName(query, exactMatch)
|
||||
|
||||
override fun registeredFlows(): List<String> = services.rpcFlows.map { it.name }.sorted()
|
||||
|
||||
|
@ -4,7 +4,10 @@ import net.corda.core.contracts.PartyAndReference
|
||||
import net.corda.core.contracts.requireThat
|
||||
import net.corda.core.crypto.subject
|
||||
import net.corda.core.crypto.toStringShort
|
||||
import net.corda.core.identity.*
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.AnonymousParty
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.node.services.IdentityService
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.utilities.loggerFor
|
||||
@ -18,6 +21,7 @@ import java.security.cert.*
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.annotation.concurrent.ThreadSafe
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
/**
|
||||
* Simple identity service which caches parties and provides functionality for efficient lookup.
|
||||
@ -95,6 +99,24 @@ class InMemoryIdentityService(identities: Iterable<PartyAndCertificate>,
|
||||
return partyFromAnonymous(party) ?: throw IllegalStateException("Could not deanonymise party ${party.owningKey.toStringShort()}")
|
||||
}
|
||||
|
||||
override fun partiesFromName(query: String, exactMatch: Boolean): Set<Party> {
|
||||
val results = HashSet<Party>()
|
||||
for ((x500name, partyAndCertificate) in principalToParties) {
|
||||
val party = partyAndCertificate.party
|
||||
for (rdn in x500name.rdNs) {
|
||||
val component = rdn.first.value.toString()
|
||||
if (exactMatch && component == query) {
|
||||
results += party
|
||||
} else if (!exactMatch) {
|
||||
// We can imagine this being a query over a lucene index in future.
|
||||
if (component.contains(query, ignoreCase = true))
|
||||
results += party
|
||||
}
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
@Throws(IdentityService.UnknownAnonymousPartyException::class)
|
||||
override fun assertOwnership(party: Party, anonymousParty: AnonymousParty) {
|
||||
val path = partyToPath[anonymousParty] ?: throw IdentityService.UnknownAnonymousPartyException("Unknown anonymous party ${anonymousParty.owningKey.toStringShort()}")
|
||||
|
@ -11,6 +11,8 @@ import net.corda.testing.ALICE_PUBKEY
|
||||
import net.corda.testing.BOB_PUBKEY
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.junit.Test
|
||||
import java.security.KeyPair
|
||||
import java.security.cert.CertPath
|
||||
import java.security.cert.X509Certificate
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
@ -53,6 +55,18 @@ class InMemoryIdentityServiceTests {
|
||||
assertNull(service.partyFromX500Name(ALICE.name))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `get identity by substring match`() {
|
||||
val service = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate)
|
||||
service.registerIdentity(ALICE_IDENTITY)
|
||||
service.registerIdentity(BOB_IDENTITY)
|
||||
val (_, _, alicente) = createParty(X500Name("O=Alicente Worldwide,L=London,C=UK"))
|
||||
service.registerIdentity(alicente)
|
||||
assertEquals(setOf(ALICE, alicente.party), service.partiesFromName("Alice", false))
|
||||
assertEquals(setOf(ALICE), service.partiesFromName("Alice Corp", true))
|
||||
assertEquals(setOf(BOB), service.partiesFromName("Bob Plc", true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `get identity by name`() {
|
||||
val service = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate)
|
||||
@ -87,12 +101,7 @@ class InMemoryIdentityServiceTests {
|
||||
*/
|
||||
@Test
|
||||
fun `assert ownership`() {
|
||||
val aliceRootKey = Crypto.generateKeyPair()
|
||||
val aliceRootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, aliceRootKey)
|
||||
val aliceTxKey = Crypto.generateKeyPair()
|
||||
val aliceTxCert = X509Utilities.createCertificate(CertificateType.IDENTITY, aliceRootCert, aliceRootKey, ALICE.name, aliceTxKey.public)
|
||||
val aliceCertPath = X509Utilities.createCertificatePath(aliceRootCert, aliceTxCert, revocationEnabled = false)
|
||||
val alice = PartyAndCertificate(ALICE.name, aliceRootKey.public, aliceRootCert, aliceCertPath)
|
||||
val (aliceTxKey, aliceCertPath, alice) = createParty(ALICE.name)
|
||||
|
||||
val bobRootKey = Crypto.generateKeyPair()
|
||||
val bobRootCert = X509Utilities.createSelfSignedCACertificate(BOB.name, bobRootKey)
|
||||
@ -120,6 +129,15 @@ class InMemoryIdentityServiceTests {
|
||||
}
|
||||
}
|
||||
|
||||
private fun createParty(x500Name: X500Name): Triple<KeyPair, CertPath, PartyAndCertificate> {
|
||||
val rootKey = Crypto.generateKeyPair()
|
||||
val rootCert = X509Utilities.createSelfSignedCACertificate(x500Name, rootKey)
|
||||
val txKey = Crypto.generateKeyPair()
|
||||
val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, rootCert, rootKey, x500Name, txKey.public)
|
||||
val certPath = X509Utilities.createCertificatePath(rootCert, txCert, revocationEnabled = false)
|
||||
return Triple(txKey, certPath, PartyAndCertificate(x500Name, rootKey.public, rootCert, certPath))
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure if we feed in a full identity, we get the same identity back.
|
||||
*/
|
||||
|
Reference in New Issue
Block a user