All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.edc.vault.azure.AzureVault Maven / Gradle / Ivy

There is a newer version: 0.9.1
Show newest version
/*
 *  Copyright (c) 2020, 2021 Microsoft Corporation
 *
 *  This program and the accompanying materials are made available under the
 *  terms of the Apache License, Version 2.0 which is available at
 *  https://www.apache.org/licenses/LICENSE-2.0
 *
 *  SPDX-License-Identifier: Apache-2.0
 *
 *  Contributors:
 *       Microsoft Corporation - initial API and implementation
 *
 */

package org.eclipse.edc.vault.azure;

import com.azure.core.exception.ResourceNotFoundException;
import com.azure.core.util.polling.SyncPoller;
import com.azure.security.keyvault.secrets.SecretClient;
import com.azure.security.keyvault.secrets.models.DeletedSecret;
import org.eclipse.edc.spi.monitor.Monitor;
import org.eclipse.edc.spi.result.Result;
import org.eclipse.edc.spi.security.Vault;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.time.Duration;
import java.util.concurrent.TimeoutException;

import static java.lang.String.format;

/**
 * Implements a vault backed by Azure Vault.
 */
public class AzureVault implements Vault {

    private static final String ALLOWED_CHARACTERS_REGEX = "^[a-zA-Z0-9-]*$";
    private static final String DISALLOWED_CHARACTERS_REGEX = "[^a-zA-Z0-9-]+";
    private static final String STARTS_WITH_LETTER_REGEX = "^[A-Za-z].*$";
    private static final String LETTER_PREFIX = "x-";
    private final SecretClient secretClient;
    private final Monitor monitor;

    public AzureVault(Monitor monitor, SecretClient secretClient) {
        this.monitor = monitor;
        this.secretClient = secretClient;
    }

    @Override
    public @Nullable String resolveSecret(String key) {
        var sanitizedKey = sanitizeKey(key);
        try {
            var secret = secretClient.getSecret(sanitizedKey);
            return secret.getValue();
        } catch (ResourceNotFoundException ex) {
            monitor.debug(format("Secret %s not found", sanitizedKey));
            return null;
        } catch (Exception ex) {
            monitor.severe("Error accessing secret " + key, ex);
            return null;
        }
    }

    @Override
    public Result storeSecret(String key, String value) {
        try {
            var sanitizedKey = sanitizeKey(key);
            secretClient.setSecret(sanitizedKey, value);
            monitor.debug("storing secret successful");
            return Result.success();
        } catch (Exception ex) {
            monitor.severe("Error storing secret", ex);
            return Result.failure(ex.getMessage());
        }
    }

    @Override
    public Result deleteSecret(String key) {
        var sanitizedKey = sanitizeKey(key);
        SyncPoller poller = null;
        try {
            poller = secretClient.beginDeleteSecret(sanitizedKey);
            monitor.debug("Begin deleting secret");
            poller.waitForCompletion(Duration.ofMinutes(1));

            monitor.debug("deletion complete");
            return Result.success();
        } catch (ResourceNotFoundException ex) {
            monitor.severe("Error deleting secret - does not exist!");
            return Result.failure(ex.getMessage());
        } catch (RuntimeException re) {
            monitor.severe("Error deleting secret", re);

            if (re.getCause() != null && re.getCause() instanceof TimeoutException) {
                try {
                    if (poller != null) {
                        poller.cancelOperation();
                    }
                } catch (Exception e) {
                    monitor.severe("Failed to abort the deletion. ", e);
                    return Result.failure(e.getMessage());
                }
            }
            return Result.failure(re.getMessage());
        } finally {
            try {
                secretClient.purgeDeletedSecret(sanitizedKey);
            } catch (Exception e) {
                monitor.severe("Error purging secret from AzureVault", e);
            }
        }
    }

    @NotNull
    private String sanitizeKey(String key) {
        if (!key.matches(STARTS_WITH_LETTER_REGEX)) {
            monitor.debug("AzureVault: key does not start with a letter. Prefixing with " + LETTER_PREFIX);
            key = LETTER_PREFIX + key;
        }
        if (!key.matches(ALLOWED_CHARACTERS_REGEX)) {
            monitor.debug("AzureVault: key contained a disallowed character. Only [a-zA-Z0-9-] are allowed. replaced with '-'");
            key = key.replaceAll(DISALLOWED_CHARACTERS_REGEX, "-");
        }
        //should we truncate the size to 127 characters or let it blow up?
        return key;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy