diff --git a/sdk/keyvault/azure-security-keyvault-certificates/pom.xml b/sdk/keyvault/azure-security-keyvault-certificates/pom.xml index b12616654d9b..36f6c171c82e 100644 --- a/sdk/keyvault/azure-security-keyvault-certificates/pom.xml +++ b/sdk/keyvault/azure-security-keyvault-certificates/pom.xml @@ -127,6 +127,7 @@ --add-exports com.azure.core/com.azure.core.implementation.http=ALL-UNNAMED --add-opens com.azure.security.keyvault.certificates/com.azure.security.keyvault.certificates=ALL-UNNAMED + --add-opens com.azure.security.keyvault.certificates/com.azure.security.keyvault.certificates.models=ALL-UNNAMED diff --git a/sdk/keyvault/azure-security-keyvault-certificates/src/main/java/com/azure/security/keyvault/certificates/CertificateClientBuilder.java b/sdk/keyvault/azure-security-keyvault-certificates/src/main/java/com/azure/security/keyvault/certificates/CertificateClientBuilder.java index 5c1c2e8d97e2..7f43800009e3 100644 --- a/sdk/keyvault/azure-security-keyvault-certificates/src/main/java/com/azure/security/keyvault/certificates/CertificateClientBuilder.java +++ b/sdk/keyvault/azure-security-keyvault-certificates/src/main/java/com/azure/security/keyvault/certificates/CertificateClientBuilder.java @@ -19,6 +19,7 @@ import com.azure.core.util.Configuration; import com.azure.core.util.logging.ClientLogger; import com.azure.security.keyvault.certificates.implementation.KeyVaultCredentialPolicy; +import com.azure.security.keyvault.certificates.models.KeyVaultCertificateIdentifier; import java.net.MalformedURLException; import java.net.URL; @@ -162,7 +163,9 @@ public CertificateAsyncClient buildAsyncClient() { /** * Sets the vault endpoint url to send HTTP requests to. * - * @param vaultUrl The vault endpoint url is used as destination on Azure to send requests to. + * @param vaultUrl The vault endpoint url is used as destination on Azure to send requests to. If you have a + * certificate identifier, use {@link KeyVaultCertificateIdentifier#parse(String)} to parse it and obtain the + * {@code vaultUrl} and other information. * @return the updated ServiceClientBuilder object. * @throws IllegalArgumentException if {@code vaultUrl} is null or it cannot be parsed into a valid URL. */ diff --git a/sdk/keyvault/azure-security-keyvault-certificates/src/main/java/com/azure/security/keyvault/certificates/models/KeyVaultCertificateIdentifier.java b/sdk/keyvault/azure-security-keyvault-certificates/src/main/java/com/azure/security/keyvault/certificates/models/KeyVaultCertificateIdentifier.java new file mode 100644 index 000000000000..493181645d99 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-certificates/src/main/java/com/azure/security/keyvault/certificates/models/KeyVaultCertificateIdentifier.java @@ -0,0 +1,105 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.certificates.models; + +import com.azure.security.keyvault.certificates.CertificateAsyncClient; +import com.azure.security.keyvault.certificates.CertificateClient; + +import java.net.MalformedURLException; +import java.net.URL; + +/** + * Information about a {@link KeyVaultCertificate} parsed from the certificate URL. You can use this information when + * calling methods of {@link CertificateClient} or {@link CertificateAsyncClient}. + */ +public final class KeyVaultCertificateIdentifier { + private final String certificateId, vaultUrl, name, version; + + private KeyVaultCertificateIdentifier(String certificateId, String vaultUrl, String name, String version) { + this.certificateId = certificateId; + this.vaultUrl = vaultUrl; + this.name = name; + this.version = version; + } + + /** + * Gets the key identifier used to create this object + * + * @return The certificate identifier. + */ + public String getCertificateId() { + return certificateId; + } + + /** + * Gets the URL of the Key Vault. + * + * @return The Key Vault URL. + */ + public String getVaultUrl() { + return vaultUrl; + } + + /** + * Gets the name of the certificate. + * + * @return The certificate name. + */ + public String getName() { + return name; + } + + /** + * Gets the optional version of the certificate. + * + * @return The certificate version. + */ + public String getVersion() { + return version; + } + + /** + * Create a new {@link KeyVaultCertificateIdentifier} from a given certificate identifier. + * + *

Valid examples are: + * + *

+ * + * @param certificateId The certificate identifier to extract information from. + * @return a new instance of {@link KeyVaultCertificateIdentifier}. + * @throws IllegalArgumentException if the given identifier is {@code null} or an invalid Key Vault Certificate + * identifier. + */ + public static KeyVaultCertificateIdentifier parse(String certificateId) throws IllegalArgumentException { + if (certificateId == null) { + throw new IllegalArgumentException("certificateId cannot be null"); + } + + try { + final URL url = new URL(certificateId); + // We expect an identifier with either 2 or 3 path segments: collection + name [+ version] + final String[] pathSegments = url.getPath().split("/"); + + if ((pathSegments.length != 3 && pathSegments.length != 4) // More or less segments in the URI than expected. + || !"https".equals(url.getProtocol()) // Invalid protocol. + || (!"certificates".equals(pathSegments[1]) && !"deletedcertificates".equals(pathSegments[1])) // Invalid collection. + || ("deletedcertificates".equals(pathSegments[1]) && pathSegments.length == 4)) { // Deleted items do not include a version. + throw new IllegalArgumentException("certificateId is not a valid Key Vault Certificate identifier"); + } + + final String vaultUrl = String.format("%s://%s", url.getProtocol(), url.getHost()); + final String certificateName = pathSegments[2]; + final String certificateVersion = pathSegments.length == 4 ? pathSegments[3] : null; + + return new KeyVaultCertificateIdentifier(certificateId, vaultUrl, certificateName, certificateVersion); + } catch (MalformedURLException e) { + throw new IllegalArgumentException("Could not parse certificateId", e); + } + } +} diff --git a/sdk/keyvault/azure-security-keyvault-certificates/src/test/java/com/azure/security/keyvault/certificates/models/KeyVaultCertificateIdentifierTest.java b/sdk/keyvault/azure-security-keyvault-certificates/src/test/java/com/azure/security/keyvault/certificates/models/KeyVaultCertificateIdentifierTest.java new file mode 100644 index 000000000000..371f16920bec --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-certificates/src/test/java/com/azure/security/keyvault/certificates/models/KeyVaultCertificateIdentifierTest.java @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.certificates.models; + +import org.junit.jupiter.api.Test; + +import java.net.MalformedURLException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class KeyVaultCertificateIdentifierTest { + @Test + void parseWithoutVersion() throws MalformedURLException { + String certificateId = "https://test-key-vault.vault.azure.net/certificates/test-certificate"; + KeyVaultCertificateIdentifier keyVaultCertificateIdentifier = + KeyVaultCertificateIdentifier.parse(certificateId); + + assertEquals(certificateId, keyVaultCertificateIdentifier.getCertificateId()); + assertEquals("https://test-key-vault.vault.azure.net", keyVaultCertificateIdentifier.getVaultUrl()); + assertEquals("test-certificate", keyVaultCertificateIdentifier.getName()); + assertNull(keyVaultCertificateIdentifier.getVersion()); + } + + @Test + void parseWithVersion() throws MalformedURLException { + String certificateId = "https://test-key-vault.vault.azure.net/certificates/test-certificate/version"; + KeyVaultCertificateIdentifier keyVaultCertificateIdentifier = + KeyVaultCertificateIdentifier.parse(certificateId); + + assertEquals(certificateId, keyVaultCertificateIdentifier.getCertificateId()); + assertEquals("https://test-key-vault.vault.azure.net", keyVaultCertificateIdentifier.getVaultUrl()); + assertEquals("test-certificate", keyVaultCertificateIdentifier.getName()); + assertEquals("version", keyVaultCertificateIdentifier.getVersion()); + } + + @Test + void parseForDeletedCertificate() throws MalformedURLException { + String certificateId = "https://test-key-vault.vault.azure.net/deletedcertificates/test-certificate"; + KeyVaultCertificateIdentifier keyVaultCertificateIdentifier = KeyVaultCertificateIdentifier.parse(certificateId); + + assertEquals(certificateId, keyVaultCertificateIdentifier.getCertificateId()); + assertEquals("https://test-key-vault.vault.azure.net", keyVaultCertificateIdentifier.getVaultUrl()); + assertEquals("test-certificate", keyVaultCertificateIdentifier.getName()); + } + + @Test + void parseInvalidIdentifierForDeletedCertificate() { + String certificateId = "https://test-key-vault.vault.azure.net/deletedcertificates/test-certificate/version"; + Exception exception = assertThrows(IllegalArgumentException.class, + () -> KeyVaultCertificateIdentifier.parse(certificateId)); + + assertEquals("certificateId is not a valid Key Vault Certificate identifier", exception.getMessage()); + } + + @Test + void parseNullIdentifier() { + Exception exception = assertThrows(IllegalArgumentException.class, + () -> KeyVaultCertificateIdentifier.parse(null)); + + assertEquals("certificateId cannot be null", exception.getMessage()); + } + + @Test + void parseInvalidIdentifierWithWrongCollection() { + String certificateId = "https://test-key-vault.vault.azure.net/keys/test-certificate"; + Exception exception = assertThrows(IllegalArgumentException.class, + () -> KeyVaultCertificateIdentifier.parse(certificateId)); + + assertEquals("certificateId is not a valid Key Vault Certificate identifier", exception.getMessage()); + } + + @Test + void parseInvalidIdentifierWithExtraSegment() { + String certificateId = "https://test-key-vault.vault.azure.net/keys/test-certificate/version/extra-segment"; + Exception exception = assertThrows(IllegalArgumentException.class, + () -> KeyVaultCertificateIdentifier.parse(certificateId)); + + assertEquals("certificateId is not a valid Key Vault Certificate identifier", exception.getMessage()); + } +} diff --git a/sdk/keyvault/azure-security-keyvault-keys/pom.xml b/sdk/keyvault/azure-security-keyvault-keys/pom.xml index 8d9903091077..add790fd73f8 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/pom.xml +++ b/sdk/keyvault/azure-security-keyvault-keys/pom.xml @@ -134,6 +134,7 @@ --add-opens com.azure.security.keyvault.keys/com.azure.security.keyvault.keys=ALL-UNNAMED --add-opens com.azure.security.keyvault.keys/com.azure.security.keyvault.keys.cryptography=ALL-UNNAMED + --add-opens com.azure.security.keyvault.keys/com.azure.security.keyvault.keys.models=ALL-UNNAMED diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyClientBuilder.java b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyClientBuilder.java index b58733b35d9d..de657f8fbff8 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyClientBuilder.java +++ b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/KeyClientBuilder.java @@ -19,6 +19,8 @@ import com.azure.core.util.CoreUtils; import com.azure.core.util.logging.ClientLogger; import com.azure.security.keyvault.keys.implementation.KeyVaultCredentialPolicy; +import com.azure.security.keyvault.keys.models.KeyVaultKeyIdentifier; + import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; @@ -163,7 +165,9 @@ public KeyAsyncClient buildAsyncClient() { /** * Sets the vault url to send HTTP requests to. * - * @param vaultUrl The vault url is used as destination on Azure to send requests to. + * @param vaultUrl The vault url is used as destination on Azure to send requests to. If you have a key identifier, + * use {@link KeyVaultKeyIdentifier#parse(String)} to parse it and obtain the {@code vaultUrl} and other + * information. * @return the updated ServiceClientBuilder object. * @throws IllegalArgumentException if {@code vaultUrl} is null or it cannot be parsed into a valid URL. */ diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/KeyVaultKeyIdentifier.java b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/KeyVaultKeyIdentifier.java new file mode 100644 index 000000000000..683c30997e19 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/src/main/java/com/azure/security/keyvault/keys/models/KeyVaultKeyIdentifier.java @@ -0,0 +1,104 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.keys.models; + +import com.azure.security.keyvault.keys.KeyAsyncClient; +import com.azure.security.keyvault.keys.KeyClient; + +import java.net.MalformedURLException; +import java.net.URL; + +/** + * Information about a {@link KeyVaultKey} parsed from the key URL. You can use this information when calling methods + * of {@link KeyClient} or {@link KeyAsyncClient}. + */ +public final class KeyVaultKeyIdentifier { + private final String keyId, vaultUrl, name, version; + + private KeyVaultKeyIdentifier(String keyId, String vaultUrl, String name, String version) { + this.keyId = keyId; + this.vaultUrl = vaultUrl; + this.name = name; + this.version = version; + } + + /** + * Gets the key identifier used to create this object. + * + * @return The key identifier. + */ + public String getKeyId() { + return keyId; + } + + /** + * Gets the URL of the Key Vault. + * + * @return The Key Vault URL. + */ + public String getVaultUrl() { + return vaultUrl; + } + + /** + * Gets the name of the key. + * + * @return The key name. + */ + public String getName() { + return name; + } + + /** + * Gets the optional version of the key. + * + * @return The key version. + */ + public String getVersion() { + return version; + } + + /** + * Create a new {@link KeyVaultKeyIdentifier} from a given key identifier. + * + *

Valid examples are: + * + *

+ * + * @param keyId The key identifier to extract information from. + * @return a new instance of {@link KeyVaultKeyIdentifier}. + * @throws IllegalArgumentException if the given identifier is {@code null} or an invalid Key Vault Key identifier. + */ + public static KeyVaultKeyIdentifier parse(String keyId) throws IllegalArgumentException { + if (keyId == null) { + throw new IllegalArgumentException("keyId cannot be null"); + } + + try { + final URL url = new URL(keyId); + // We expect an identifier with either 2 or 3 path segments: collection + name [+ version] + final String[] pathSegments = url.getPath().split("/"); + + if ((pathSegments.length != 3 && pathSegments.length != 4) // More or less segments in the URI than expected. + || !"https".equals(url.getProtocol()) // Invalid protocol. + || (!"keys".equals(pathSegments[1]) && !"deletedkeys".equals(pathSegments[1])) // Invalid collection. + || ("deletedkeys".equals(pathSegments[1]) && pathSegments.length == 4)) { // Deleted items do not include a version. + throw new IllegalArgumentException("keyId is not a valid Key Vault Key identifier"); + } + + final String vaultUrl = String.format("%s://%s", url.getProtocol(), url.getHost()); + final String keyName = pathSegments[2]; + final String keyVersion = pathSegments.length == 4 ? pathSegments[3] : null; + + return new KeyVaultKeyIdentifier(keyId, vaultUrl, keyName, keyVersion); + } catch (MalformedURLException e) { + throw new IllegalArgumentException("Could not parse keyId", e); + } + } +} diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/test/java/com/azure/security/keyvault/keys/models/KeyVaultKeyIdentifierTest.java b/sdk/keyvault/azure-security-keyvault-keys/src/test/java/com/azure/security/keyvault/keys/models/KeyVaultKeyIdentifierTest.java new file mode 100644 index 000000000000..0b067d469305 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/src/test/java/com/azure/security/keyvault/keys/models/KeyVaultKeyIdentifierTest.java @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.keys.models; + +import org.junit.jupiter.api.Test; + +import java.net.MalformedURLException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class KeyVaultKeyIdentifierTest { + @Test + void parseWithoutVersion() throws MalformedURLException { + String keyId = "https://test-key-vault.vault.azure.net/keys/test-key"; + KeyVaultKeyIdentifier keyVaultKeyIdentifier = KeyVaultKeyIdentifier.parse(keyId); + + assertEquals(keyId, keyVaultKeyIdentifier.getKeyId()); + assertEquals("https://test-key-vault.vault.azure.net", keyVaultKeyIdentifier.getVaultUrl()); + assertEquals("test-key", keyVaultKeyIdentifier.getName()); + assertNull(keyVaultKeyIdentifier.getVersion()); + } + + @Test + void parseWithVersion() throws MalformedURLException { + String keyId = "https://test-key-vault.vault.azure.net/keys/test-key/version"; + KeyVaultKeyIdentifier keyVaultkeyIdentifier = KeyVaultKeyIdentifier.parse(keyId); + + assertEquals(keyId, keyVaultkeyIdentifier.getKeyId()); + assertEquals("https://test-key-vault.vault.azure.net", keyVaultkeyIdentifier.getVaultUrl()); + assertEquals("test-key", keyVaultkeyIdentifier.getName()); + assertEquals("version", keyVaultkeyIdentifier.getVersion()); + } + + @Test + void parseForDeletedKey() throws MalformedURLException { + String keyId = "https://test-key-vault.vault.azure.net/deletedkeys/test-key"; + KeyVaultKeyIdentifier keyVaultKeyIdentifier = KeyVaultKeyIdentifier.parse(keyId); + + assertEquals(keyId, keyVaultKeyIdentifier.getKeyId()); + assertEquals("https://test-key-vault.vault.azure.net", keyVaultKeyIdentifier.getVaultUrl()); + assertEquals("test-key", keyVaultKeyIdentifier.getName()); + } + + @Test + void parseInvalidIdentifierForDeletedKey() { + String keyId = "https://test-key-vault.vault.azure.net/deletedkeys/test-key/version"; + Exception exception = assertThrows(IllegalArgumentException.class, () -> KeyVaultKeyIdentifier.parse(keyId)); + + assertEquals("keyId is not a valid Key Vault Key identifier", exception.getMessage()); + } + + @Test + void parseNullIdentifier() { + Exception exception = assertThrows(IllegalArgumentException.class, () -> KeyVaultKeyIdentifier.parse(null)); + + assertEquals("keyId cannot be null", exception.getMessage()); + } + + @Test + void parseInvalidIdentifierWithWrongCollection() { + String keyId = "https://test-key-vault.vault.azure.net/secrets/test-key"; + Exception exception = assertThrows(IllegalArgumentException.class, () -> KeyVaultKeyIdentifier.parse(keyId)); + + assertEquals("keyId is not a valid Key Vault Key identifier", exception.getMessage()); + } + + @Test + void parseInvalidIdentifierWithExtraSegment() { + String keyId = "https://test-key-vault.vault.azure.net/keys/test-key/version/extra"; + Exception exception = assertThrows(IllegalArgumentException.class, () -> KeyVaultKeyIdentifier.parse(keyId)); + + assertEquals("keyId is not a valid Key Vault Key identifier", exception.getMessage()); + } +} diff --git a/sdk/keyvault/azure-security-keyvault-secrets/pom.xml b/sdk/keyvault/azure-security-keyvault-secrets/pom.xml index 0e92b0619a20..ea0e09f4963c 100644 --- a/sdk/keyvault/azure-security-keyvault-secrets/pom.xml +++ b/sdk/keyvault/azure-security-keyvault-secrets/pom.xml @@ -134,6 +134,7 @@ --add-exports com.azure.core/com.azure.core.implementation.http=ALL-UNNAMED --add-opens com.azure.security.keyvault.secrets/com.azure.security.keyvault.secrets=ALL-UNNAMED + --add-opens com.azure.security.keyvault.secrets/com.azure.security.keyvault.secrets.models=ALL-UNNAMED diff --git a/sdk/keyvault/azure-security-keyvault-secrets/src/main/java/com/azure/security/keyvault/secrets/SecretClientBuilder.java b/sdk/keyvault/azure-security-keyvault-secrets/src/main/java/com/azure/security/keyvault/secrets/SecretClientBuilder.java index 669720ee1341..5a530a705d2d 100644 --- a/sdk/keyvault/azure-security-keyvault-secrets/src/main/java/com/azure/security/keyvault/secrets/SecretClientBuilder.java +++ b/sdk/keyvault/azure-security-keyvault-secrets/src/main/java/com/azure/security/keyvault/secrets/SecretClientBuilder.java @@ -19,6 +19,7 @@ import com.azure.core.util.CoreUtils; import com.azure.core.util.logging.ClientLogger; import com.azure.security.keyvault.secrets.implementation.KeyVaultCredentialPolicy; +import com.azure.security.keyvault.secrets.models.KeyVaultSecretIdentifier; import java.net.MalformedURLException; import java.net.URL; @@ -166,7 +167,9 @@ public SecretAsyncClient buildAsyncClient() { /** * Sets the vault url to send HTTP requests to. * - * @param vaultUrl The vault url is used as destination on Azure to send requests to. + * @param vaultUrl The vault url is used as destination on Azure to send requests to. If you have a secret + * identifier, use {@link KeyVaultSecretIdentifier#parse(String)} to parse it and obtain the {@code vaultUrl} and + * other information. * @return the updated {@link SecretClientBuilder} object. * @throws IllegalArgumentException if {@code vaultUrl} is null or it cannot be parsed into a valid URL. */ diff --git a/sdk/keyvault/azure-security-keyvault-secrets/src/main/java/com/azure/security/keyvault/secrets/models/KeyVaultSecretIdentifier.java b/sdk/keyvault/azure-security-keyvault-secrets/src/main/java/com/azure/security/keyvault/secrets/models/KeyVaultSecretIdentifier.java new file mode 100644 index 000000000000..8192ac6aeede --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-secrets/src/main/java/com/azure/security/keyvault/secrets/models/KeyVaultSecretIdentifier.java @@ -0,0 +1,105 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.secrets.models; + +import com.azure.security.keyvault.secrets.SecretAsyncClient; +import com.azure.security.keyvault.secrets.SecretClient; + +import java.net.MalformedURLException; +import java.net.URL; + +/** + * Information about a {@link KeyVaultSecret} parsed from the secret URL. You can use this information when calling + * methods of {@link SecretClient} or {@link SecretAsyncClient}. + */ +public final class KeyVaultSecretIdentifier { + private final String secretId, vaultUrl, name, version; + + private KeyVaultSecretIdentifier(String secretId, String vaultUrl, String name, String version) { + this.secretId = secretId; + this.vaultUrl = vaultUrl; + this.name = name; + this.version = version; + } + + /** + * Gets the key identifier used to create this object + * + * @return The secret identifier. + */ + public String getSecretId() { + return secretId; + } + + /** + * Gets the URL of the Key Vault. + * + * @return The Key Vault URL. + */ + public String getVaultUrl() { + return vaultUrl; + } + + /** + * Gets the name of the secret. + * + * @return The secret name. + */ + public String getName() { + return name; + } + + /** + * Gets the optional version of the secret. + * + * @return The secret version. + */ + public String getVersion() { + return version; + } + + /** + * Create a new {@link KeyVaultSecretIdentifier} from a given secret identifier. + * + *

Valid examples are: + * + *

+ * + * @param secretId The secret identifier to extract information from. + * @return a new instance of {@link KeyVaultSecretIdentifier}. + * @throws IllegalArgumentException if the given identifier is {@code null} or an invalid Key Vault Secret + * identifier. + */ + public static KeyVaultSecretIdentifier parse(String secretId) throws IllegalArgumentException { + if (secretId == null) { + throw new IllegalArgumentException("secretId cannot be null"); + } + + try { + final URL url = new URL(secretId); + // We expect an identifier with either 2 or 3 path segments: collection + name [+ version] + final String[] pathSegments = url.getPath().split("/"); + + if ((pathSegments.length != 3 && pathSegments.length != 4) // More or less segments in the URI than expected. + || !"https".equals(url.getProtocol()) // Invalid protocol. + || (!"secrets".equals(pathSegments[1]) && !"deletedsecrets".equals(pathSegments[1])) // Invalid collection. + || ("deletedsecrets".equals(pathSegments[1]) && pathSegments.length == 4)) { // Deleted items do not include a version. + throw new IllegalArgumentException("secretId is not a valid Key Vault Secret identifier"); + } + + final String vaultUrl = String.format("%s://%s", url.getProtocol(), url.getHost()); + final String secretName = pathSegments[2]; + final String secretVersion = pathSegments.length == 4 ? pathSegments[3] : null; + + return new KeyVaultSecretIdentifier(secretId, vaultUrl, secretName, secretVersion); + } catch (MalformedURLException e) { + throw new IllegalArgumentException("Could not parse secretId", e); + } + } +} diff --git a/sdk/keyvault/azure-security-keyvault-secrets/src/test/java/com/azure/security/keyvault/secrets/models/KeyVaultSecretIdentifierTest.java b/sdk/keyvault/azure-security-keyvault-secrets/src/test/java/com/azure/security/keyvault/secrets/models/KeyVaultSecretIdentifierTest.java new file mode 100644 index 000000000000..c3c1f4f50add --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-secrets/src/test/java/com/azure/security/keyvault/secrets/models/KeyVaultSecretIdentifierTest.java @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.secrets.models; + +import org.junit.jupiter.api.Test; + +import java.net.MalformedURLException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class KeyVaultSecretIdentifierTest { + @Test + void parseWithoutVersion() throws MalformedURLException { + String secretId = "https://test-key-vault.vault.azure.net/secrets/test-secret"; + KeyVaultSecretIdentifier keyVaultSecretIdentifier = KeyVaultSecretIdentifier.parse(secretId); + + assertEquals(secretId, keyVaultSecretIdentifier.getSecretId()); + assertEquals("https://test-key-vault.vault.azure.net", keyVaultSecretIdentifier.getVaultUrl()); + assertEquals("test-secret", keyVaultSecretIdentifier.getName()); + assertNull(keyVaultSecretIdentifier.getVersion()); + } + + @Test + void parseWithVersion() throws MalformedURLException { + String secretId = "https://test-key-vault.vault.azure.net/secrets/test-secret/version"; + KeyVaultSecretIdentifier keyVaultSecretIdentifier = KeyVaultSecretIdentifier.parse(secretId); + + assertEquals(secretId, keyVaultSecretIdentifier.getSecretId()); + assertEquals("https://test-key-vault.vault.azure.net", keyVaultSecretIdentifier.getVaultUrl()); + assertEquals("test-secret", keyVaultSecretIdentifier.getName()); + assertEquals("version", keyVaultSecretIdentifier.getVersion()); + } + + @Test + void parseForDeletedSecret() throws MalformedURLException { + String secretId = "https://test-key-vault.vault.azure.net/deletedsecrets/test-secret"; + KeyVaultSecretIdentifier keyVaultSecretIdentifier = KeyVaultSecretIdentifier.parse(secretId); + + assertEquals(secretId, keyVaultSecretIdentifier.getSecretId()); + assertEquals("https://test-key-vault.vault.azure.net", keyVaultSecretIdentifier.getVaultUrl()); + assertEquals("test-secret", keyVaultSecretIdentifier.getName()); + } + + @Test + void parseInvalidIdentifierForDeletedSecret() { + String secretId = "https://test-key-vault.vault.azure.net/deletedsecrets/test-secret/version"; + Exception exception = assertThrows(IllegalArgumentException.class, () -> KeyVaultSecretIdentifier.parse(secretId)); + + assertEquals("secretId is not a valid Key Vault Secret identifier", exception.getMessage()); + } + + @Test + void parseNullIdentifier() { + Exception exception = assertThrows(IllegalArgumentException.class, () -> KeyVaultSecretIdentifier.parse(null)); + + assertEquals("secretId cannot be null", exception.getMessage()); + } + + @Test + void parseInvalidIdentifierWithWrongCollection() { + String secretId = "https://test-key-vault.vault.azure.net/certificates/test-secret"; + Exception exception = assertThrows(IllegalArgumentException.class, + () -> KeyVaultSecretIdentifier.parse(secretId)); + + assertEquals("secretId is not a valid Key Vault Secret identifier", exception.getMessage()); + } + + @Test + void parseInvalidIdentifierWithExtraSegment() { + String secretId = "https://test-key-vault.vault.azure.net/secrets/test-secret/version/extra"; + Exception exception = assertThrows(IllegalArgumentException.class, + () -> KeyVaultSecretIdentifier.parse(secretId)); + + assertEquals("secretId is not a valid Key Vault Secret identifier", exception.getMessage()); + } +}