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

com.microsoft.azure.documentdb.internal.BaseAuthorizationTokenProvider Maven / Gradle / Ivy

/* 
 * Copyright (c) Microsoft Corporation.  All rights reserved.
 */

package com.microsoft.azure.documentdb.internal;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;

/**
 * This class is used internally by both client (for generating the auth header with master/system key) and by the Gateway when
 * verifying the auth header in the Azure Cosmos DB database service.
 */
public class BaseAuthorizationTokenProvider implements AuthorizationTokenProvider {

    private String masterKey;
    private Map resourceTokens;
    private Mac macInstance;

    public BaseAuthorizationTokenProvider(String masterKey, Map resourceTokens) {
        this.masterKey = masterKey;
        this.resourceTokens = resourceTokens;
        // masterkey could be null if authorization is done with permission tokens
        if (this.masterKey != null) {
            byte[] masterKeyDecodedBytes = Base64.decodeBase64(this.masterKey.getBytes());
            SecretKey signingKey = new SecretKeySpec(masterKeyDecodedBytes, "HMACSHA256");
            try {
                this.macInstance = Mac.getInstance("HMACSHA256");
                this.macInstance.init(signingKey);
            } catch (NoSuchAlgorithmException | InvalidKeyException e) {
                throw new IllegalStateException(e);
            }
        }
    }

    /**
     * This API is a helper method to create auth header based on client request using masterkey.
     *
     * @param verb                 the verb.
     * @param resourceIdOrFullName the resource id or full name
     * @param resourceType         the resource type.
     * @param headers              the request headers.
     * @return the key authorization signature.
     */
    public String generateKeyAuthorizationSignature(String verb,
                                                    String resourceIdOrFullName,
                                                    ResourceType resourceType,
                                                    Map headers) {
        return this.generateKeyAuthorizationSignature(verb, resourceIdOrFullName,
                Utils.getResourceSegement(resourceType).toLowerCase(), headers);
    }

    /**
     * This API is a helper method to create auth header based on client request using masterkey.
     *
     * @param verb                 the verb
     * @param resourceIdOrFullName the resource id or full name
     * @param  resourceSegment     the resource segment
     * @param headers              the request headers
     * @return the key authorization signature
     */
    public String generateKeyAuthorizationSignature(String verb,
                                                    String resourceIdOrFullName,
                                                    String resourceSegment,
                                                    Map headers) {
        if (verb == null || verb.isEmpty()) {
            throw new IllegalArgumentException("verb");
        }

        if (resourceIdOrFullName == null) {
            resourceIdOrFullName = "";
        }

        if (resourceSegment == null) {
            throw new IllegalArgumentException("resourceSegment");
        }

        if (headers == null) {
            throw new IllegalArgumentException("headers");
        }

        if (this.masterKey == null || this.masterKey.isEmpty()) {
            throw new IllegalArgumentException("masterKey");
        }

        if(!PathsHelper.isNameBased(resourceIdOrFullName)) {
            resourceIdOrFullName = resourceIdOrFullName.toLowerCase(Locale.ROOT);
        }

        // Skipping lower casing of resourceId since it may now contain "ID" of the resource as part of the FullName
        String body = String.format("%s\n%s\n%s\n",
                verb.toLowerCase(),
                resourceSegment,
                resourceIdOrFullName);

        if (headers.containsKey(HttpConstants.HttpHeaders.X_DATE)) {
            body += headers.get(HttpConstants.HttpHeaders.X_DATE).toLowerCase();
        }

        body += '\n';

        if (headers.containsKey(HttpConstants.HttpHeaders.HTTP_DATE)) {
            body += headers.get(HttpConstants.HttpHeaders.HTTP_DATE).toLowerCase();
        }

        body += '\n';

        Mac mac = null;
        try {
            mac = (Mac) this.macInstance.clone();
        } catch (CloneNotSupportedException e) {
            throw new IllegalStateException(e);
        }

        byte[] digest = mac.doFinal(body.getBytes());

        String auth = Utils.encodeBase64String(digest);

        String authtoken = "type=master&ver=1.0&sig=" + auth;

        return authtoken;
    }

    /**
     * This API is a helper method to create auth header based on client request using resourceTokens.
     *
     * @param resourceTokens the resource tokens.
     * @param path           the path.
     * @param resourceId     the resource id.
     * @return the authorization token.
     */
    public String getAuthorizationTokenUsingResourceTokens(String path,
                                                           String resourceId) {
        if (resourceTokens == null) {
            throw new IllegalArgumentException("resourceTokens");
        }

        String resourceToken = null;
        if (resourceTokens.containsKey(resourceId) && resourceTokens.get(resourceId) != null) {
            resourceToken = resourceTokens.get(resourceId);
        } else if (StringUtils.isEmpty(path) || StringUtils.isEmpty(resourceId)) {
            if (resourceTokens.size() > 0) {
                resourceToken = resourceTokens.values().iterator().next();
            }
        } else {
            // Get the last resource id from the path and use that to find the corresponding token.
            String[] pathParts = StringUtils.split(path, '/');
            String[] resourceTypes = {"dbs", "colls", "docs", "sprocs", "udfs", "triggers", "users", "permissions",
                    "attachments", "media", "conflicts"};
            HashSet resourceTypesSet = new HashSet();
            for (String resourceType : resourceTypes) {
                resourceTypesSet.add(resourceType);
            }

            for (int i = pathParts.length - 1; i >= 0; --i) {

                if (!resourceTypesSet.contains(pathParts[i]) && resourceTokens.containsKey(pathParts[i])) {
                    resourceToken = resourceTokens.get(pathParts[i]);
                }
            }
        }

        return resourceToken;
    }

    @Override
    public String getMasterKey() {
        return this.masterKey;
    }

    @Override
    public Map getResourceTokens() {
        return this.resourceTokens;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy