ConfigurationCredentialsPolicy.java

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.data.appconfiguration.implementation;

import com.azure.core.http.HttpPipelineCallContext;
import com.azure.core.http.HttpPipelineNextPolicy;
import com.azure.core.http.HttpResponse;
import com.azure.core.http.policy.HttpPipelinePolicy;
import com.azure.data.appconfiguration.ConfigurationAsyncClient;
import com.azure.data.appconfiguration.ConfigurationClientBuilder;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.ByteBuffer;
import java.util.Objects;

/**
 * A policy that authenticates requests with Azure App Configuration service. The content added by this policy
 * is leveraged in {@link ConfigurationClientCredentials} to generate the correct "Authorization" header value.
 *
 * @see ConfigurationClientCredentials
 * @see ConfigurationAsyncClient
 * @see ConfigurationClientBuilder
 */
public final class ConfigurationCredentialsPolicy implements HttpPipelinePolicy {
    // "Host", "Date", and "x-ms-content-sha256" are required to generate "Authorization" value in
    // ConfigurationClientCredentials.

    private final ConfigurationClientCredentials credentials;

    /**
     * Creates an instance that is able to apply a {@link ConfigurationClientCredentials} credential to a request in the
     * pipeline.
     *
     * @param credentials the credential information to authenticate to Azure App Configuration service
     * @throws NullPointerException If {@code credential} is {@code null}.
     */
    public ConfigurationCredentialsPolicy(ConfigurationClientCredentials credentials) {
        Objects.requireNonNull(credentials, "'credential' can not be a null value.");
        this.credentials = credentials;
    }

    /**
     * Adds the required headers to authenticate a request to Azure App Configuration service.
     *
     * @param context The request context
     * @param next The next HTTP pipeline policy to process the {@code context's} request after this policy
     *     completes.
     * @return A {@link Mono} representing the HTTP response that will arrive asynchronously.
     */
    @Override
    public Mono<HttpResponse> process(HttpPipelineCallContext context, HttpPipelineNextPolicy next) {
        final Flux<ByteBuffer> contents = context.getHttpRequest().getBody() == null
            ? Flux.just(getEmptyBuffer())
            : context.getHttpRequest().getBody();

        return credentials
            .getAuthorizationHeadersAsync(
                context.getHttpRequest().getUrl(),
                context.getHttpRequest().getHttpMethod().toString(),
                contents.defaultIfEmpty(getEmptyBuffer()))
            .flatMapMany(headers -> Flux.fromIterable(headers.entrySet()))
            .map(header -> context.getHttpRequest().setHeader(header.getKey(), header.getValue()))
            .last()
            .flatMap(request -> next.process());
    }

    private ByteBuffer getEmptyBuffer() {
        return ByteBuffer.allocate(0);
    }
}