/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*/
package com.microsoft.azure.keyvault.test;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import org.joda.time.DateTime;
import org.junit.Assert;
import org.junit.Test;
import com.microsoft.azure.keyvault.models.Attributes;
import com.microsoft.azure.keyvault.models.KeyVaultError;
import com.microsoft.azure.keyvault.models.KeyVaultErrorException;
import com.microsoft.azure.keyvault.models.SecretAttributes;
import com.microsoft.azure.keyvault.models.SecretBundle;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.microsoft.azure.PagedList;
import com.microsoft.azure.keyvault.SecretIdentifier;
import com.microsoft.azure.keyvault.models.SecretItem;
import com.microsoft.azure.keyvault.requests.SetSecretRequest;
import com.microsoft.azure.keyvault.requests.UpdateSecretRequest;
public class SecretOperationsTest extends KeyVaultClientIntegrationTestBase {
private static final String SECRET_NAME = "javaSecret";
private static final String SECRET_VALUE = "Chocolate is hidden in the toothpaste cabinet";
private static final int MAX_SECRETS = 4;
private static final int PAGELIST_MAX_SECRETS = 3;
@Test
public void transparentAuthentication() throws Exception {
// Create a secret on a vault.
{
Attributes attributes = new SecretAttributes()
.withEnabled(true)
.withExpires(new DateTime().withYear(2050).withMonthOfYear(1))
.withNotBefore(new DateTime().withYear(2000).withMonthOfYear(1));
Map<String, String> tags = new HashMap<String, String>();
tags.put("foo", "baz");
String contentType = "contentType";
SecretBundle secret = keyVaultClient.setSecret(
new SetSecretRequest
.Builder(getVaultUri(), SECRET_NAME, SECRET_VALUE)
.withAttributes(attributes)
.withContentType(contentType)
.withTags(tags)
.build());
validateSecret(secret, getVaultUri(), SECRET_NAME, SECRET_VALUE, contentType, attributes);
}
// Create a secret on a different vault. Secret Vault Data Plane returns
// 401, which must be transparently handled by KeyVaultCredentials.
{
SecretBundle secret = keyVaultClient.setSecret(
new SetSecretRequest.Builder(getSecondaryVaultUri(), SECRET_NAME, SECRET_VALUE).build());
validateSecret(secret, getSecondaryVaultUri(), SECRET_NAME, SECRET_VALUE, null, null);
}
}
@Test
public void deserializeWithExtraFieldTest() throws Exception {
String content = "{\"error\":{\"code\":\"SecretNotFound\",\"message\":\"Secret not found: javaSecret\",\"noneexisting\":true}}";
KeyVaultError error = keyVaultClient.serializerAdapter().deserialize(content, KeyVaultError.class);
Assert.assertEquals(error.error().message(), "Secret not found: javaSecret");
Assert.assertEquals(error.error().code(), "SecretNotFound");
}
@Test
// verifies the inner error on disabled secret
public void disabledSecretGet() throws Exception {
String secretName = "disabledsecret";
SecretBundle secret = keyVaultClient.setSecret(
new SetSecretRequest
.Builder(getVaultUri(), secretName, SECRET_VALUE)
.withAttributes(new SecretAttributes().withEnabled(false))
.build());
try {
keyVaultClient.getSecret(secret.id());
Assert.fail("Should throw exception for disabled secret.");
}
catch (KeyVaultErrorException e) {
Assert.assertEquals(e.body().error().code(), "Forbidden");
Assert.assertNotNull(e.body().error().message());
Assert.assertNotNull(e.body().error().innerError());
Assert.assertEquals(e.body().error().innerError().code(), "SecretDisabled");
}
catch (Exception e) {
Assert.fail("Should throw KeyVaultErrorException for disabled secret.");
}
keyVaultClient.deleteSecret(getVaultUri(), secretName);
}
@Test
public void crudOperations() throws Exception {
SecretBundle secret;
{
// Create secret
secret = keyVaultClient.setSecret(
new SetSecretRequest.Builder(getVaultUri(), SECRET_NAME, SECRET_VALUE).build());
validateSecret(secret, getVaultUri(), SECRET_NAME, SECRET_VALUE, null, null);
}
// Secret identifier.
SecretIdentifier secretId = new SecretIdentifier(secret.id());
{
// Get secret using kid WO version
SecretBundle readBundle = keyVaultClient.getSecret(secretId.baseIdentifier());
compareSecrets(secret, readBundle);
}
{
// Get secret using full kid as defined in the bundle
SecretBundle readBundle = keyVaultClient.getSecret(secret.id());
compareSecrets(secret, readBundle);
}
{
// Get secret using vault and secret name.
SecretBundle readBundle = keyVaultClient.getSecret(getVaultUri(), SECRET_NAME);
compareSecrets(secret, readBundle);
}
{
// Get secret using vault, secret name and version.
SecretBundle readBundle = keyVaultClient.getSecret(getVaultUri(), SECRET_NAME, secretId.version());
compareSecrets(secret, readBundle);
}
{
secret.attributes().withExpires(new DateTime()
.withMonthOfYear(2)
.withDayOfMonth(1)
.withYear(2050));
Map<String, String> tags = new HashMap<String, String>();
tags.put("foo", "baz");
secret.withTags(tags)
.withContentType("application/html")
.withValue(null); // The value doesn't get updated
// Update secret using the kid as defined in the bundle
SecretBundle updatedSecret = keyVaultClient.updateSecret(
new UpdateSecretRequest
.Builder(secret.id())
.withContentType(secret.contentType())
.withAttributes(secret.attributes())
.withTags(secret.tags())
.build());
compareSecrets(secret, updatedSecret);
// Subsequent operations must use the updated bundle for comparison.
secret = updatedSecret;
}
{
// Update secret using vault and secret name.
secret.attributes().withNotBefore(new DateTime()
.withMonthOfYear(2)
.withDayOfMonth(1)
.withYear(2000));
Map<String, String> tags = new HashMap<String, String>();
tags.put("rex", "woof");
secret.withTags(tags)
.withContentType("application/html");
// Perform the operation.
SecretBundle updatedSecret = keyVaultClient.updateSecret(
new UpdateSecretRequest
.Builder(getVaultUri(), SECRET_NAME)
.withVersion(secret.secretIdentifier().version())
.withContentType(secret.contentType())
.withAttributes(secret.attributes())
.withTags(secret.tags())
.build());
compareSecrets(secret, updatedSecret);
validateSecret(updatedSecret,
secret.secretIdentifier().vault(),
secret.secretIdentifier().name(),
null, secret.contentType(), secret.attributes());
}
{
// Delete secret
SecretBundle deleteBundle = keyVaultClient.deleteSecret(getVaultUri(), SECRET_NAME);
compareSecrets(secret, deleteBundle);
}
{
// Expects a secret not found
try {
keyVaultClient.getSecret(secretId.baseIdentifier());
} catch (KeyVaultErrorException e) {
Assert.assertNotNull(e.body().error().code());
Assert.assertEquals("SecretNotFound", e.body().error().code());
}
}
}
@Test
public void listSecrets() throws Exception {
HashSet<String> secrets = new HashSet<String>();
for (int i = 0; i < MAX_SECRETS; ++i) {
int failureCount = 0;
for (;;) {
try {
SecretBundle secret = keyVaultClient.setSecret(
new SetSecretRequest.Builder(getVaultUri(), SECRET_NAME + i, SECRET_VALUE).build());
SecretIdentifier id = new SecretIdentifier(secret.id());
secrets.add(id.baseIdentifier());
break;
} catch (KeyVaultErrorException e) {
++failureCount;
if (e.body().error().code().equals("Throttled")) {
System.out.println("Waiting to avoid throttling");
Thread.sleep(failureCount * 1500);
continue;
}
throw e;
}
}
}
PagedList<SecretItem> listResult = keyVaultClient.listSecrets(getVaultUri(), PAGELIST_MAX_SECRETS);
Assert.assertTrue(PAGELIST_MAX_SECRETS >= listResult.currentPage().items().size());
HashSet<String> toDelete = new HashSet<String>();
for (SecretItem item : listResult) {
if(item != null) {
SecretIdentifier id = new SecretIdentifier(item.id());
toDelete.add(id.name());
secrets.remove(item.id());
}
}
Assert.assertEquals(0, secrets.size());
for (String secretName : toDelete) {
try{
keyVaultClient.deleteSecret(getVaultUri(), secretName);
}
catch(KeyVaultErrorException e){
// Ignore forbidden exception for certificate secrets that cannot be deleted
if(!e.body().error().code().equals("Forbidden"))
throw e;
}
}
}
@Test
public void listSecretVersions() throws Exception {
HashSet<String> secrets = new HashSet<String>();
for (int i = 0; i < MAX_SECRETS; ++i) {
int failureCount = 0;
for (;;) {
try {
SecretBundle secret = keyVaultClient.setSecret(
new SetSecretRequest.Builder(getVaultUri(), SECRET_NAME, SECRET_VALUE).build());
secrets.add(secret.id());
break;
} catch (KeyVaultErrorException e) {
++failureCount;
if (e.body().error().code().equals("Throttled")) {
System.out.println("Throttled!");
Thread.sleep(failureCount * 1500);
continue;
}
throw e;
}
}
}
PagedList<SecretItem> listResult = keyVaultClient.listSecretVersions(getVaultUri(), SECRET_NAME, PAGELIST_MAX_SECRETS);
Assert.assertTrue(PAGELIST_MAX_SECRETS >= listResult.currentPage().items().size());
listResult = keyVaultClient.listSecretVersions(getVaultUri(), SECRET_NAME);
for (SecretItem item : listResult) {
if(item != null) {
secrets.remove(item.id());
}
}
Assert.assertEquals(0, secrets.size());
keyVaultClient.deleteSecret(getVaultUri(), SECRET_NAME);
}
private static void validateSecret(SecretBundle secret, String vault, String name, String value, String contentType, Attributes attributes) throws Exception {
String prefix = vault + "/secrets/" + name + "/";
String id = secret.id();
Assert.assertTrue( //
String.format("\"id\" should start with \"%s\", but instead the value is \"%s\".", prefix, id), //
id.startsWith(prefix));
Assert.assertEquals(value, secret.value());
if (contentType != null) {
Assert.assertEquals(contentType, secret.contentType());
}
Assert.assertNotNull("\"created\" should not be null.", secret.attributes().created());
Assert.assertNotNull("\"updated\" should not be null.", secret.attributes().updated());
compareAttributes(attributes, secret.attributes());
Assert.assertTrue(secret.managed() == null || secret.managed() == false);
}
private void compareSecrets(SecretBundle expected, SecretBundle actual) {
Assert.assertEquals(expected.contentType(), actual.contentType());
Assert.assertEquals(expected.id(), actual.id());
Assert.assertEquals(expected.value(), actual.value());
Assert.assertEquals(expected.attributes().enabled(), actual.attributes().enabled());
Assert.assertEquals(expected.attributes().expires(), actual.attributes().expires());
Assert.assertEquals(expected.attributes().notBefore(), actual.attributes().notBefore());
if(expected.tags() != null || actual.tags() != null)
Assert.assertTrue(expected.tags().equals(actual.tags()));
}
}