diff --git a/core/src/test/kotlin/net/corda/core/identity/CordaX500NameTest.kt b/core/src/test/kotlin/net/corda/core/identity/CordaX500NameTest.kt index 3cfc7c089b..53dc7e186a 100644 --- a/core/src/test/kotlin/net/corda/core/identity/CordaX500NameTest.kt +++ b/core/src/test/kotlin/net/corda/core/identity/CordaX500NameTest.kt @@ -5,6 +5,8 @@ import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertNull +import java.lang.Character.MIN_VALUE as NULLCHAR + class CordaX500NameTest { @Test fun `service name with organisational unit`() { @@ -60,17 +62,128 @@ class CordaX500NameTest { } } - @Test - fun `rejects name with wrong organisation name format`() { - assertFailsWith(IllegalArgumentException::class) { - CordaX500Name.parse("O=B, L=New York, C=US, OU=Org Unit, CN=Service Name") - } - } - @Test fun `rejects name with unsupported attribute`() { assertFailsWith(IllegalArgumentException::class) { CordaX500Name.parse("O=Bank A, L=New York, C=US, SN=blah") } } + + @Test + fun `rejects organisation (but not other attributes) with non-latin letters`() { + assertFailsWith(IllegalArgumentException::class) { + CordaX500Name.parse("O=Bཛྷa, L=New York, C=DE, OU=Org Unit, CN=Service Name") + } + // doesn't throw + validateLocalityAndOrganisationalUnitAndCommonName("Bཛྷa") + } + + @Test + fun `organisation (but not other attributes) must have at least two letters`() { + assertFailsWith(IllegalArgumentException::class) { + CordaX500Name.parse("O=B, L=New York, C=DE, OU=Org Unit, CN=Service Name") + } + assertFailsWith(IllegalArgumentException::class) { + CordaX500Name.parse("O=, L=New York, C=DE, OU=Org Unit, CN=Service Name") + } + // doesn't throw + validateLocalityAndOrganisationalUnitAndCommonName("B") + validateLocalityAndOrganisationalUnitAndCommonName("") + } + + @Test + fun `accepts attributes starting with lower case letter`() { + CordaX500Name.parse("O=bank A, L=New York, C=DE, OU=Org Unit, CN=Service Name") + validateLocalityAndOrganisationalUnitAndCommonName("bank") + } + + @Test + fun `accepts attributes starting with numeric character`() { + CordaX500Name.parse("O=8Bank A, L=New York, C=DE, OU=Org Unit, CN=Service Name") + validateLocalityAndOrganisationalUnitAndCommonName("8bank") + } + + @Test + fun `accepts attributes with leading whitespace`() { + CordaX500Name.parse("O= VALID, L=VALID, C=DE, OU=VALID, CN=VALID") + validateLocalityAndOrganisationalUnitAndCommonName(" VALID") + } + + @Test + fun `accepts attributes with trailing whitespace`() { + CordaX500Name.parse("O=VALID , L=VALID, C=DE, OU=VALID, CN=VALID") + validateLocalityAndOrganisationalUnitAndCommonName("VALID ") + } + + @Test + fun `rejects attributes with comma`() { + assertFailsWith(IllegalArgumentException::class) { + CordaX500Name.parse("O=IN,VALID, L=VALID, C=DE, OU=VALID, CN=VALID") + } + checkLocalityAndOrganisationalUnitAndCommonNameReject("IN,VALID") + } + + @Test + fun `accepts org with equals sign`() { + CordaX500Name.parse("O=IN=VALID, L=VALID, C=DE, OU=VALID, CN=VALID") + } + + @Test + fun `accepts organisation with dollar sign`() { + CordaX500Name.parse("O=VA\$LID, L=VALID, C=DE, OU=VALID, CN=VALID") + validateLocalityAndOrganisationalUnitAndCommonName("VA\$LID") + } + @Test + fun `rejects attributes with double quotation mark`() { + assertFailsWith(IllegalArgumentException::class) { + CordaX500Name.parse("O=IN\"VALID, L=VALID, C=DE, OU=VALID, CN=VALID") + } + checkLocalityAndOrganisationalUnitAndCommonNameReject("IN\"VALID") + } + + @Test + fun `accepts organisation with single quotation mark`() { + CordaX500Name.parse("O=VA'LID, L=VALID, C=DE, OU=VALID, CN=VALID") + validateLocalityAndOrganisationalUnitAndCommonName("VA'LID") + } + @Test + fun `rejects organisation with backslash`() { + assertFailsWith(IllegalArgumentException::class) { + CordaX500Name.parse("O=IN\\VALID, L=VALID, C=DE, OU=VALID, CN=VALID") + } + checkLocalityAndOrganisationalUnitAndCommonNameReject("IN\\VALID") + } + + @Test + fun `rejects double spacing only in the organisation attribute`() { + assertFailsWith(IllegalArgumentException::class) { + CordaX500Name.parse("O=IN VALID , L=VALID, C=DE, OU=VALID, CN=VALID") + } + validateLocalityAndOrganisationalUnitAndCommonName("VA LID") + } + @Test + fun `rejects organisation (but not other attributes) containing the null character`() { + assertFailsWith(IllegalArgumentException::class) { + CordaX500Name.parse("O=IN${NULLCHAR}VALID , L=VALID, C=DE, OU=VALID, CN=VALID") + } + validateLocalityAndOrganisationalUnitAndCommonName("VA${NULLCHAR}LID") + } + + fun checkLocalityAndOrganisationalUnitAndCommonNameReject(invalid: String) { + assertFailsWith(IllegalArgumentException::class) { + CordaX500Name.parse("O=VALID, L=${invalid}, C=DE, OU=VALID, CN=VALID") + } + assertFailsWith(IllegalArgumentException::class) { + CordaX500Name.parse("O=VALID, L=VALID, C=DE, OU=${invalid}, CN=VALID") + } + assertFailsWith(IllegalArgumentException::class) { + CordaX500Name.parse("O=VALID, L=VALID, C=DE, OU=VALID, CN=${invalid}") + } + } + + fun validateLocalityAndOrganisationalUnitAndCommonName(valid: String) { + CordaX500Name.parse("O=VALID, L=${valid}, C=DE, OU=VALID, CN=VALID") + CordaX500Name.parse("O=VALID, L=VALID, C=DE, OU=${valid}, CN=VALID") + CordaX500Name.parse("O=VALID, L=VALID, C=DE, OU=VALID, CN=${valid}") + } } diff --git a/docs/source/node-structure.rst b/docs/source/node-structure.rst index 374548f8ee..9d785df0a9 100644 --- a/docs/source/node-structure.rst +++ b/docs/source/node-structure.rst @@ -71,22 +71,18 @@ The name must also obey the following constraints: * The ``country`` attribute is a valid ISO 3166-1 two letter code in upper-case -* All attributes must obey the following constraints: - - * Upper-case first letter +* The ``organisation`` field of the name obeys the following constraints: * Has at least two letters - * No leading or trailing whitespace - * Does not include the following characters: ``,`` , ``=`` , ``$`` , ``"`` , ``'`` , ``\`` + * Does not include the following characters: ``,`` , ``"``, ``\`` * Is in NFKC normalization form * Does not contain the null character * Only the latin, common and inherited unicode scripts are supported - -* The ``organisation`` field of the name also obeys the following constraints: - * No double-spacing - * This is to avoid right-to-left issues, debugging issues when we can't pronounce names over the phone, and - character confusability attacks +This is to avoid right-to-left issues, debugging issues when we can't pronounce names over the phone, and +character confusability attacks. + +.. note:: The network operator of a Corda Network may put additional constraints on node naming in place. External identifiers ^^^^^^^^^^^^^^^^^^^^