com.emc.atmos.api.RestUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of atmos-client Show documentation
Show all versions of atmos-client Show documentation
EMC Atmos Client for Java - provides REST access to object data on EMC platforms using the Atmos API.
/*
* BSD 3-Clause License
*
* Copyright (c) 2013-2018, Dell EMC
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package com.emc.atmos.api;
import com.emc.atmos.AtmosException;
import com.emc.atmos.api.bean.Metadata;
import com.emc.atmos.api.bean.Permission;
import com.emc.util.HttpUtil;
import org.apache.log4j.Logger;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class RestUtil {
public static final String HEADER_CONTENT_TYPE = "Content-Type";
public static final String HEADER_DATE = "Date";
public static final String HEADER_EXPECT = "Expect";
public static final String HEADER_RANGE = "Range";
public static final String HEADER_ETAG = "ETag";
public static final String HEADER_LAST_MODIFIED = "Last-Modified";
public static final String XHEADER_CONTENT_CHECKSUM = "x-emc-content-checksum";
public static final String XHEADER_DATE = "x-emc-date";
public static final String XHEADER_EXPIRES = "x-emc-expires";
public static final String XHEADER_FEATURES = "x-emc-features";
public static final String XHEADER_FORCE = "x-emc-force";
public static final String XHEADER_GENERATE_CHECKSUM = "x-emc-generate-checksum";
public static final String XHEADER_GROUP_ACL = "x-emc-groupacl";
public static final String XHEADER_INCLUDE_META = "x-emc-include-meta";
public static final String XHEADER_LIMIT = "x-emc-limit";
public static final String XHEADER_LISTABLE_META = "x-emc-listable-meta";
public static final String XHEADER_LISTABLE_TAGS = "x-emc-listable-tags";
public static final String XHEADER_META = "x-emc-meta";
public static final String XHEADER_OBJECT_ID = "x-emc-object-id";
public static final String XHEADER_OBJECTID = "x-emc-objectid";
public static final String XHEADER_PATH = "x-emc-path";
public static final String XHEADER_POOL = "x-emc-pool";
public static final String XHEADER_RETENTION_PERIOD = "x-emc-retention-period";
public static final String XHEADER_RETENTION_POLICY = "x-emc-retention-policy";
public static final String XHEADER_SIGNATURE = "x-emc-signature";
public static final String XHEADER_SUBTENANT_ID = "x-emc-subtenant-id";
public static final String XHEADER_SUPPORT_UTF8 = "x-emc-support-utf8";
public static final String XHEADER_SYSTEM_TAGS = "x-emc-system-tags";
public static final String XHEADER_TAGS = "x-emc-tags";
public static final String XHEADER_TOKEN = "x-emc-token";
public static final String XHEADER_UID = "x-emc-uid";
public static final String XHEADER_USER_ACL = "x-emc-useracl";
public static final String XHEADER_USER_TAGS = "x-emc-user-tags";
public static final String XHEADER_UTF8 = "x-emc-utf8";
public static final String XHEADER_VERSION_OID = "x-emc-version-oid";
public static final String XHEADER_WSCHECKSUM = "x-emc-wschecksum";
public static final String XHEADER_OBJECT_VPOOL = "x-emc-vpool";
private static final String USER_MAUI_PREFIX = "user.maui.";
public static final String METADATA_KEY_EXPIRATION_ENABLE = USER_MAUI_PREFIX + "expirationEnable";
public static final String METADATA_KEY_EXPIRATION_END = USER_MAUI_PREFIX + "expirationEnd";
public static final String METADATA_KEY_RETENTION_ENABLE = USER_MAUI_PREFIX + "retentionEnable";
public static final String METADATA_KEY_RETENTION_END = USER_MAUI_PREFIX + "retentionEnd";
public static final String TYPE_MULTIPART = "multipart";
public static final String TYPE_MULTIPART_BYTE_RANGES = "multipart/byteranges";
public static final String TYPE_APPLICATION_OCTET_STREAM = "application/octet-stream";
public static final String TYPE_DEFAULT = TYPE_APPLICATION_OCTET_STREAM;
public static final String TYPE_PARAM_BOUNDARY = "boundary";
public static final String PROP_ENABLE_EXPECT_100_CONTINUE = "com.emc.atmos.api.expect100Continue";
private static final Logger l4j = Logger.getLogger( RestUtil.class );
private static final Pattern OBJECTID_PATTERN = Pattern.compile( "/\\w+/objects/([0-9a-f-]{44,})" );
public static String sign( String string, byte[] hashKey ) {
try {
// Compute the signature hash
l4j.debug( "Hashing: \n" + string );
byte[] input = string.getBytes( "UTF-8" );
Mac mac = Mac.getInstance( "HmacSHA1" );
SecretKeySpec key = new SecretKeySpec( hashKey, "HmacSHA1" );
mac.init( key );
byte[] hashBytes = mac.doFinal( input );
// Encode the hash in Base64.
String hash = DatatypeConverter.printBase64Binary(hashBytes);
l4j.debug( "Hash: " + hash );
return hash;
} catch ( Exception e ) {
throw new RuntimeException( "Error signing string:\n" + string + "\n", e );
}
}
/**
* Generates the HMAC-SHA1 signature used to authenticate the request using
* the Java security APIs, then adds the uid and signature to the headers.
*
* @param method the HTTP method used
* @param path the resource path including any querystring
* @param headers the HTTP headers for the request
* @param hashKey the secret key to use when signing
*/
public static void signRequest( String method, String path, String query, Map> headers,
String uid, byte[] hashKey, long serverClockSkew ) {
// Add date header
Date serverTime = new Date( System.currentTimeMillis() - serverClockSkew );
headers.put( HEADER_DATE, Arrays.asList( (Object) HttpUtil.headerFormat( serverTime ) ) );
headers.put( XHEADER_DATE, Arrays.asList( (Object) HttpUtil.headerFormat( serverTime ) ) );
// Add uid to headers
if ( !headers.containsKey( XHEADER_UID ) )
headers.put( XHEADER_UID, Arrays.asList( (Object) uid ) );
// Build the string to hash.
StringBuilder builder = new StringBuilder();
builder.append( method ).append( "\n" );
// Add the following header values or blank lines if they aren't present
builder.append( generateHashLine( headers, HEADER_CONTENT_TYPE ) );
builder.append( generateHashLine( headers, HEADER_RANGE ) );
builder.append( generateHashLine( headers, HEADER_DATE ) );
// Add the resource
builder.append( path.toLowerCase() );
if ( query != null ) builder.append( "?" ).append( query );
builder.append( "\n" );
// Do the 'x-emc' headers. The headers must be hashed in alphabetic
// order and the values must be stripped of whitespace and newlines.
// TreeMap will automatically sort by key.
Map emcHeaders = new TreeMap();
for ( String key : headers.keySet() ) {
String lowerKey = key.toLowerCase();
if ( lowerKey.indexOf( "x-emc" ) == 0 )
emcHeaders.put( lowerKey, join( headers.get( key ), "," ) );
}
for ( Iterator i = emcHeaders.keySet().iterator(); i.hasNext(); ) {
String key = i.next();
builder.append( key ).append( ':' ).append( normalizeSpace( emcHeaders.get( key ) ) );
if ( i.hasNext() ) builder.append( "\n" );
}
String hash = sign( builder.toString(), hashKey );
// Add signature to headers
headers.put( XHEADER_SIGNATURE, Arrays.asList( (Object) hash ) );
}
public static String normalizeSpace( String str ) {
int length;
do {
length = str.length();
str = str.replace( " ", " " );
} while ( length != str.length() );
return str.replace( "\n", "" ).trim();
}
public static String join( Iterable> list, String delimiter ) {
if ( list == null ) return null;
StringBuilder builder = new StringBuilder();
for ( Iterator> i = list.iterator(); i.hasNext(); ) {
Object value = i.next();
builder.append( value );
if ( i.hasNext() ) builder.append( delimiter );
}
return builder.toString();
}
/**
* Initializes new keys with an empty ArrayList. Convenience method for generating a header map.
*/
public static void addValue( Map> multiValueMap, String key, Object value ) {
List