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

org.opensearch.common.blobstore.EncryptedBlobContainer Maven / Gradle / Ivy

There is a newer version: 2.18.0
Show newest version
/*
 * SPDX-License-Identifier: Apache-2.0
 *
 * The OpenSearch Contributors require contributions made to
 * this file be licensed under the Apache-2.0 license or a
 * compatible open source license.
 */

package org.opensearch.common.blobstore;

import org.opensearch.common.CheckedBiConsumer;
import org.opensearch.common.annotation.ExperimentalApi;
import org.opensearch.common.crypto.CryptoHandler;
import org.opensearch.common.crypto.DecryptedRangedStreamProvider;
import org.opensearch.common.crypto.EncryptedHeaderContentSupplier;
import org.opensearch.common.io.InputStreamContainer;
import org.opensearch.core.action.ActionListener;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * EncryptedBlobContainer is a wrapper around BlobContainer that encrypts the data on the fly.
 */
public class EncryptedBlobContainer implements BlobContainer {

    private final BlobContainer blobContainer;
    private final CryptoHandler cryptoHandler;

    public EncryptedBlobContainer(BlobContainer blobContainer, CryptoHandler cryptoHandler) {
        this.blobContainer = blobContainer;
        this.cryptoHandler = cryptoHandler;
    }

    @Override
    public BlobPath path() {
        return blobContainer.path();
    }

    @Override
    public boolean blobExists(String blobName) throws IOException {
        return blobContainer.blobExists(blobName);
    }

    @Override
    public InputStream readBlob(String blobName) throws IOException {
        InputStream inputStream = blobContainer.readBlob(blobName);
        return cryptoHandler.createDecryptingStream(inputStream);
    }

    @ExperimentalApi
    @Override
    public InputStreamWithMetadata readBlobWithMetadata(String blobName) throws IOException {
        InputStreamWithMetadata inputStreamWithMetadata = blobContainer.readBlobWithMetadata(blobName);
        InputStream decryptInputStream = cryptoHandler.createDecryptingStream(inputStreamWithMetadata.getInputStream());
        return new InputStreamWithMetadata(decryptInputStream, inputStreamWithMetadata.getMetadata());
    }

    EncryptedHeaderContentSupplier getEncryptedHeaderContentSupplier(String blobName) {
        return (start, end) -> {
            byte[] buffer;
            int length = (int) (end - start + 1);
            try (InputStream inputStream = blobContainer.readBlob(blobName, start, length)) {
                buffer = new byte[length];
                inputStream.readNBytes(buffer, (int) start, buffer.length);
            }
            return buffer;
        };
    }

    @Override
    public InputStream readBlob(String blobName, long position, long length) throws IOException {
        U encryptionMetadata = cryptoHandler.loadEncryptionMetadata(getEncryptedHeaderContentSupplier(blobName));
        DecryptedRangedStreamProvider decryptedStreamProvider = cryptoHandler.createDecryptingStreamOfRange(
            encryptionMetadata,
            position,
            position + length - 1
        );
        long adjustedPos = decryptedStreamProvider.getAdjustedRange()[0];
        long adjustedLength = decryptedStreamProvider.getAdjustedRange()[1] - adjustedPos + 1;
        InputStream encryptedStream = blobContainer.readBlob(blobName, adjustedPos, adjustedLength);
        return decryptedStreamProvider.getDecryptedStreamProvider().apply(encryptedStream);
    }

    @Override
    public long readBlobPreferredLength() {
        return blobContainer.readBlobPreferredLength();
    }

    private void executeWrite(InputStream inputStream, long blobSize, CheckedBiConsumer writeConsumer)
        throws IOException {
        T cryptoContext = cryptoHandler.initEncryptionMetadata();
        InputStreamContainer streamContainer = new InputStreamContainer(inputStream, blobSize, 0);
        InputStreamContainer encryptedStream = cryptoHandler.createEncryptingStream(cryptoContext, streamContainer);
        long cryptoLength = cryptoHandler.estimateEncryptedLengthOfEntireContent(cryptoContext, blobSize);
        writeConsumer.accept(encryptedStream.getInputStream(), cryptoLength);
    }

    @Override
    public void writeBlob(String blobName, InputStream inputStream, long blobSize, boolean failIfAlreadyExists) throws IOException {
        executeWrite(
            inputStream,
            blobSize,
            (encryptedStream, encryptedLength) -> blobContainer.writeBlob(blobName, encryptedStream, encryptedLength, failIfAlreadyExists)
        );
    }

    @Override
    public void writeBlobAtomic(String blobName, InputStream inputStream, long blobSize, boolean failIfAlreadyExists) throws IOException {
        executeWrite(
            inputStream,
            blobSize,
            (encryptedStream, encryptedLength) -> blobContainer.writeBlobAtomic(
                blobName,
                encryptedStream,
                encryptedLength,
                failIfAlreadyExists
            )
        );
    }

    @Override
    public DeleteResult delete() throws IOException {
        return blobContainer.delete();
    }

    @Override
    public void deleteBlobsIgnoringIfNotExists(List blobNames) throws IOException {
        blobContainer.deleteBlobsIgnoringIfNotExists(blobNames);
    }

    @Override
    public Map listBlobs() throws IOException {
        Map blobMetadataMap = blobContainer.listBlobs();
        return convertToEncryptedMetadataMap(blobMetadataMap);
    }

    @Override
    public Map children() throws IOException {
        Map children = blobContainer.children();
        if (children != null) {
            return children.entrySet()
                .stream()
                .collect(Collectors.toMap(Map.Entry::getKey, entry -> new EncryptedBlobContainer<>(entry.getValue(), cryptoHandler)));
        } else {
            return null;
        }
    }

    @Override
    public Map listBlobsByPrefix(String blobNamePrefix) throws IOException {
        Map blobMetadataMap = blobContainer.listBlobsByPrefix(blobNamePrefix);
        return convertToEncryptedMetadataMap(blobMetadataMap);
    }

    private Map convertToEncryptedMetadataMap(Map blobMetadataMap) {
        if (blobMetadataMap == null) {
            return null;
        }

        return blobMetadataMap.entrySet()
            .stream()
            .collect(
                Collectors.toMap(
                    Map.Entry::getKey,
                    entry -> new EncryptedBlobMetadata<>(entry.getValue(), cryptoHandler, getEncryptedHeaderContentSupplier(entry.getKey()))
                )
            );

    }

    @Override
    public void listBlobsByPrefixInSortedOrder(
        String blobNamePrefix,
        int limit,
        BlobNameSortOrder blobNameSortOrder,
        ActionListener> listener
    ) {
        ActionListener> encryptedMetadataListener = ActionListener.delegateFailure(
            listener,
            (delegatedListener, metadataList) -> {
                if (metadataList != null) {
                    List encryptedMetadata = metadataList.stream()
                        .map(
                            blobMetadata -> new EncryptedBlobMetadata<>(
                                blobMetadata,
                                cryptoHandler,
                                getEncryptedHeaderContentSupplier(blobMetadata.name())
                            )
                        )
                        .collect(Collectors.toList());
                    delegatedListener.onResponse(encryptedMetadata);
                } else {
                    delegatedListener.onResponse(null);
                }
            }
        );
        blobContainer.listBlobsByPrefixInSortedOrder(blobNamePrefix, limit, blobNameSortOrder, encryptedMetadataListener);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy