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;
}
}