/*************************************************************************
* Copyright 2009-2015 Eucalyptus Systems, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
* Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta
* CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need
* additional information or have any questions.
*
* This file may incorporate work covered under the following copyright
* and permission notice:
*
* Software License Agreement (BSD License)
*
* Copyright (c) 2008, Regents of the University of California
* All rights reserved.
*
* Redistribution and use of this software 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.
*
* 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 OWNER 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. USERS OF THIS SOFTWARE ACKNOWLEDGE
* THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE LICENSED MATERIAL,
* COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS SOFTWARE,
* AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
* IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA,
* SANTA BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY,
* WHICH IN THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION,
* REPLACEMENT OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO
* IDENTIFIED, OR WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT
* NEEDED TO COMPLY WITH ANY SUCH LICENSES OR RIGHTS.
************************************************************************/
package com.eucalyptus.auth.login;
import static org.junit.Assert.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.security.auth.Subject;
import org.junit.Test;
import com.eucalyptus.auth.AuthException;
import com.eucalyptus.auth.InvalidSignatureAuthException;
import com.eucalyptus.auth.principal.AccessKey;
import com.eucalyptus.auth.principal.UserPrincipal;
import com.eucalyptus.crypto.Hmac;
import com.eucalyptus.crypto.util.B64;
import com.eucalyptus.crypto.util.SecurityParameter;
import com.eucalyptus.util.CollectionUtils;
import com.eucalyptus.ws.util.HmacUtils;
import com.google.common.base.Charsets;
import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets;
import com.google.common.io.ByteStreams;
import com.google.common.io.Resources;
/**
* Unit tests for HMAC login modules
*/
public class HmacLoginModuleTest {
@Test
public void testUrlDecode() throws Exception {
final String signature = B64.standard.encString(new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 0});
final String decoded = loginModule().urldecode( URLEncoder.encode( signature, "UTF-8" ) );
assertEquals( "URL decoded value", signature, decoded );
}
@Test
public void testNormalize() throws Exception {
testNormalize( "Normalized input", "A+Y=", "A+Y=" );
testNormalize( "Truncated input", "A+Y", "A+Y=" );
testNormalize( "Truncated input 2", "BBBBAw", "BBBBAw==" );
testNormalize( "URL encoded", "A%2bY=", "A+Y=" );
testNormalize( "Sanitized embedded", "=A=+=Y=", "A+Y=" );
testNormalize( "Sanitized trailing", "A+Y=====", "A+Y=" );
}
private void testNormalize( final String desc, final String signature, final String expectedNormalized ) {
final String normalized = loginModule().normalize( signature );
assertEquals( desc, expectedNormalized, normalized );
}
@Test
public void testAcceptanceHmacV2() throws Exception {
final HmacCredentials credsV2 = creds(
"MrFSyGZ44/Oe4nOfXQImKmq8oRABMrmNk2mJIWz1dCA=",
Maps.newHashMap( ImmutableMap.of(
"AWSAccessKeyId", "1234567890",
"SignatureVersion", "2"
) ),
"GET",
"/path",
"localhost:8773",
2,
Hmac.HmacSHA256 );
final Hmacv2LoginModule loginModule = hmacV2LoginModule();
loginModule.initialize( new Subject(), credsV2, Collections.<String,String>emptyMap(), Collections.<String,String>emptyMap() );
assertTrue("Module accepts credentials", loginModule.accepts());
final HmacCredentials credsV1 = creds(
"MrFSyGZ44/Oe4nOfXQImKmq8oRABMrmNk2mJIWz1dCA=",
Maps.newHashMap(ImmutableMap.of(
"AWSAccessKeyId", "1234567890",
"SignatureVersion", "1"
)),
"GET",
"/path",
"localhost:8773",
1,
Hmac.HmacSHA256 );
loginModule.reset();
loginModule.initialize( new Subject(), credsV1, Collections.<String,String>emptyMap(), Collections.<String,String>emptyMap() );
assertFalse( "Module rejects credentials", loginModule.accepts() );
}
@Test
public void testBasicHmacV2() throws Exception {
final HmacCredentials creds = creds(
"MrFSyGZ44/Oe4nOfXQImKmq8oRABMrmNk2mJIWz1dCA=",
Maps.newHashMap(ImmutableMap.of(
"AWSAccessKeyId", "1234567890",
"SignatureVersion", "2"
)),
"GET",
"/path",
"localhost:8773",
2,
Hmac.HmacSHA256);
assertTrue( "Authentication successful", hmacV2LoginModule().authenticate( creds ) );
}
@Test
public void testHmacV2LexicographicalOrdering() throws Exception {
final HmacCredentials creds1 = creds(
"ZB1nE9O2Nun8iCcCQXiKAasZZ/ZSl0yqoQ/+Ux70f2U=",
new LinkedHashMap<String,String>(){{
put( "AWSAccessKeyId", "1234567890" );
put( "SignatureVersion", "2" );
put( "R", "1" );
put( "A", "1" );
}},
"GET", "/path", "localhost:8773",
2,
Hmac.HmacSHA256);
assertTrue("Authentication successful", hmacV2LoginModule().authenticate(creds1));
}
@Test
public void testHmacV2LexicographicalByteOrderingWithCase() throws Exception {
final HmacCredentials creds1 = creds(
"Yxtxh7wgZgI98Ps9cQa9hQ5A6WlwDMrlz10PjVk6xgY=",
new LinkedHashMap<String,String>(){{
put( "AWSAccessKeyId", "1234567890" );
put( "SignatureVersion", "2" );
put( "a", "1" );
put( "A", "1" );
}},
"GET", "/path", "localhost:8773",
2,
Hmac.HmacSHA256);
assertTrue("Authentication successful", hmacV2LoginModule().authenticate(creds1));
final HmacCredentials creds2 = creds(
"Yxtxh7wgZgI98Ps9cQa9hQ5A6WlwDMrlz10PjVk6xgY=",
new LinkedHashMap<String,String>(){{
put( "AWSAccessKeyId", "1234567890" );
put( "SignatureVersion", "2" );
put( "A", "1" );
put( "a", "1" );
}},
"GET", "/path", "localhost:8773",
2,
Hmac.HmacSHA256);
assertTrue("Authentication successful", hmacV2LoginModule().authenticate(creds2));
}
@Test
public void testHmacV2NoValue() throws Exception {
final HmacCredentials creds1 = creds(
"hU6YeOTMqVgax+hLoDQTqjVyi09XaadIUrp9PHNOXug=",
new LinkedHashMap<String,String>(){{
put( "AWSAccessKeyId", "1234567890" );
put( "SignatureVersion", "2" );
put( "NP", null );
}},
"GET", "/path", "localhost:8773",
2,
Hmac.HmacSHA256);
assertTrue("Authentication successful", hmacV2LoginModule().authenticate(creds1));
}
@Test
public void testHmacV2ExtraEquals() throws Exception {
final HmacCredentials creds = creds(
"MrFSyGZ44/Oe4nOfXQImKmq8oRABMrmNk2mJIWz1dCA=====",
Maps.newHashMap(ImmutableMap.of(
"AWSAccessKeyId", "1234567890",
"SignatureVersion", "2"
)),
"GET",
"/path",
"localhost:8773",
2,
Hmac.HmacSHA256);
assertTrue("Authentication successful", hmacV2LoginModule().authenticate(creds));
}
@Test
public void testHmacV2NoPort() throws Exception {
final HmacCredentials creds = creds(
"J0TPfG6W7ZFd7Di+0oeXPdIKUjw00dKR+HUB7nZAMV4=",
Maps.newHashMap(ImmutableMap.of(
"AWSAccessKeyId", "1234567890",
"SignatureVersion", "2"
)),
"GET",
"/path",
"localhost:8773",
2,
Hmac.HmacSHA256 );
assertTrue("Authentication successful", hmacV2LoginModule().authenticate(creds));
}
@Test
public void testBasicHmacV2PlusAsEncodedPlusCanonicalization() throws Exception {
final HmacCredentials creds = creds(
"HcHSowrJLwxbym2DFAhVhkXR8mFZ2UcDtKg9hh1Qbyc=",
Maps.newHashMap(ImmutableMap.of(
"AWSAccessKeyId", "1234567890",
"SignatureVersion", "2"
)),
"GET",
"/path+plus",
"localhost:8773",
2,
Hmac.HmacSHA256 );
assertTrue("Authentication successful", hmacV2LoginModule().authenticate(creds));
}
@Test
public void testBasicHmacV2PlusAsEncodedSpaceCanonicalization() throws Exception {
final HmacCredentials creds = creds(
"FOoBqskkKoiCj0duRQmjetxJCUgrAx8vwW neYG2WJs=",
Maps.newHashMap(ImmutableMap.of(
"AWSAccessKeyId", "1234567890",
"SignatureVersion", "2"
)),
"GET",
"/path+plus",
"localhost:8773",
2,
Hmac.HmacSHA256 );
assertTrue("Authentication successful", hmacV2LoginModule().authenticate(creds));
}
@Test
public void testBasicHmacV2StarEncodedCanonicalization() throws Exception {
final HmacCredentials creds = creds(
"03E5iuj6n3jg9h6lYiR5IPtsvwwZX0dhsQeGy kZeZY=",
Maps.newHashMap(ImmutableMap.of(
"AWSAccessKeyId", "1234567890*",
"SignatureVersion", "2",
"Star", "*"
)),
"GET",
"/path*star",
"localhost:8773",
2,
Hmac.HmacSHA256 );
assertTrue("Authentication successful", hmacV2LoginModule().authenticate(creds));
}
@Test
public void testBasicHmacV2HostCanonicalization() throws Exception {
final HmacCredentials creds = creds(
"1Y44avkQ78kH7ryAElD9ZpJmzbytRinIclJJZUNgvwE=",
Maps.newHashMap(ImmutableMap.<String,String>builder( )
.put( "AWSAccessKeyId", "AKIAAFOSB6X4B4FC52B3" )
.put( "Action", "DescribeLoadBalancerPolicyTypes" )
.put( "SignatureMethod", "HmacSHA256" )
.put( "SignatureVersion", "2" )
.put( "Timestamp", "2015-05-20T05:17:48Z" )
.put( "Version", "2012-06-01" )
.build( ) ),
"POST",
"/services/LoadBalancing",
"LOCALHOST:8773",
2,
Hmac.HmacSHA256 );
assertTrue("Authentication successful", hmacV2LoginModule( "xBPMQG8zlU6zWHO3kvXnosnVxKdGROK2BDHOOMZw" ).authenticate(creds));
}
@Test
public void testHmacV2UrlEncoded() throws Exception {
final HmacCredentials creds = creds(
"MrFSyGZ44%2fOe4nOfXQImKmq8oRABMrmNk2mJIWz1dCA%3d",
Maps.newHashMap(ImmutableMap.of(
"AWSAccessKeyId", "1234567890",
"SignatureVersion", "2"
)),
"GET",
"/path",
"localhost:8773",
2,
Hmac.HmacSHA256 );
assertTrue("Authentication successful", hmacV2LoginModule().authenticate(creds));
}
@Test
public void testHmacV2WithMethod() throws Exception {
final HmacCredentials creds = creds(
"6FoWdyXxxjCgqKYVsjpvbY6ol6ddfzUbJgwi7SRuyIA=",
Maps.newHashMap(ImmutableMap.of(
"AWSAccessKeyId", "1234567890",
"SignatureVersion", "2"
)),
"DELETE",
"/path",
"localhost:8773",
2,
Hmac.HmacSHA256 );
assertTrue("Authentication successful", hmacV2LoginModule().authenticate(creds));
}
@Test( expected = InvalidSignatureAuthException.class )
public void testHmacV2Invalid() throws Exception {
final HmacCredentials creds = creds(
"MrFSyGZ44%2fOe4nOfXQImKmR8oRABMrmNk2mJIWz1dCA%3d",
Maps.newHashMap(ImmutableMap.of(
"AWSAccessKeyId", "1234567890",
"SignatureVersion", "2"
)),
"GET",
"/path",
"localhost:8773",
2,
Hmac.HmacSHA256 );
hmacV2LoginModule().authenticate( creds );
}
/**
* Hybridfox version 1.7 build 000177 (EC2 API 2011-12-01)
*/
@Test
public void testHybridfox_1_7_000177_Sigv2() throws Exception {
final HmacCredentials creds = creds(
"L9mcIT2K/SOnOg09t01DWCKtadNOoIEGo/PkpW/DL08=",
Maps.newHashMap(ImmutableMap.<String,String>builder()
.put( "Action", "DescribeRegions" )
.put( "AWSAccessKeyId", "1234567890" )
.put( "SignatureMethod", "HmacSHA256" )
.put( "SignatureVersion", "2" )
.put( "Timestamp", "2012-05-09T23:10:15" )
.put( "Version", "2011-12-01" )
.build()),
"POST", "/service/Eucalyptus/", "localhost:8773",
2,
Hmac.HmacSHA256);
assertTrue("Authentication successful", hmacV2LoginModule().authenticate(creds));
}
/**
* Euca2ools / Boto
* euca2ools-1.3.1-12.fc16.noarch
* Version: 1.2 (BSD)
* User-Agent: Boto/2.0 (linux2)
*/
@Test
public void testEuca2oolsBoto_1_3_1() throws Exception {
final HmacCredentials creds = creds(
"JjRHXf60U4eatEOpKhf0enPNimNzVKLy99f0+/lUfzc=",
Maps.newHashMap(ImmutableMap.<String,String>builder()
.put( "AWSAccessKeyId", "1234567890" )
.put( "Action", "DescribeInstances" )
.put( "SignatureMethod", "HmacSHA256" )
.put( "SignatureVersion", "2" )
.put( "Timestamp", "2012-05-10T18:00:00Z" )
.put( "Version", "2011-01-01" )
.build()),
"POST", "/services/Eucalyptus/", "localhost:8773",
2,
Hmac.HmacSHA256);
assertTrue("Authentication successful", hmacV2LoginModule().authenticate(creds));
}
/**
* Typica 1.7.2
*
* Note: Typica does not include the port in the host header - "Host: localhost"
*/
@Test
public void testTypica_1_7_2_Sigv2() throws Exception {
final HmacCredentials creds = creds(
"PotQrmC/3ZTCGaRIZnlg0cX9VFUgiB+aguCJorntDhI=",
Maps.newHashMap(ImmutableMap.<String,String>builder()
.put( "AWSAccessKeyId", "1234567890" )
.put( "Action", "DescribeImages" )
.put( "SignatureMethod", "HmacSHA256" )
.put( "SignatureVersion", "2" )
.put( "Timestamp", "2012-05-10T18:53:00Z" )
.put( "Version", "2010-06-15" )
.build()),
"GET", "/services/Eucalyptus", "localhost:8773",
2,
Hmac.HmacSHA256);
assertTrue("Authentication successful", hmacV2LoginModule().authenticate(creds));
}
/**
* AWS Java SDK 1.3.27
*/
@Test
public void testAWSJavaSDK_1_3_27_Sigv2() throws Exception {
final HmacCredentials creds = creds(
"Xn7WHmdCX7+QqJDWD/7Fau5lIgQoV8tq5gcLq2TTyk0=",
Maps.newHashMap(ImmutableMap.<String,String>builder()
.put( "Action", "TerminateInstances" )
.put( "SignatureMethod", "HmacSHA256" )
.put( "InstanceId.1", "i-C7D241F0" )
.put( "AWSAccessKeyId", "M2JFNG1R6BH2WXWC4GEAE" )
.put( "SignatureVersion", "2" )
.put( "Version", "2012-12-01" )
.put( "Timestamp", "2013-03-04T20:44:58.852Z" )
.build()),
"POST", "/services/Eucalyptus", "10.111.1.65:8773",
2,
Hmac.HmacSHA256);
assertTrue("Authentication successful", hmacV2LoginModule("LuB6vcOGvBFkUYTGMd7CtwTJlgIN1BprPo9yfSoe").authenticate(creds));
}
/**
* Amazon CLI/AutoScaling 1.0.61.2 API 2011-01-01
*
* NOTE: Signature is invalid due to signed path being "/services/autoscaling"
*/
@Test
public void testAWSAutoScalingCLI_1_0_61_2_Sigv2() throws Exception {
final HmacCredentials creds = creds(
"ILCkibsyRv/4nsUToJnKMZAjSrYTx++UFfuxXERyCkg=",
Maps.newHashMap(ImmutableMap.<String,String>builder()
.put( "MaxRecords", "20" )
.put( "Version", "2011-01-01" )
.put( "Action", "DescribeAutoScalingGroups" )
.put( "SignatureVersion", "2" )
.put( "SignatureMethod", "HmacSHA256" )
.put( "Timestamp", "2013-04-04T01:47:05.199Z" )
.put( "AWSAccessKeyId", "7C6LEIR2VTIXHPTXTCEAD" )
.build()),
"GET", "/services/AutoScaling/", "10.111.1.116:8773",
2,
Hmac.HmacSHA256);
assertTrue("Authentication successful", hmacV2LoginModule("7pG138tHUyt5YjOd4gRLZSOX6Nn77skFg29r1SQk").authenticate(creds));
}
/**
* Amazon CLI/CloudWatch 1.0.13.4 API 2010-08-01
*
* NOTE: Signature is invalid due to signed path being "/services/cloudwatch"
*/
@Test
public void testAWSCloudWatchCLI_1_0_13_4_Sigv2() throws Exception {
final HmacCredentials creds = creds(
"JEDUx0NOenXpnidyKzH/Ut+HCzqcFF3l0icDeXJ8lOw=",
Maps.newHashMap(ImmutableMap.<String,String>builder()
.put( "Version", "2010-08-01" )
.put( "Action", "DeleteAlarms" )
.put( "SignatureVersion", "2" )
.put( "SignatureMethod", "HmacSHA256" )
.put( "Timestamp", "2013-04-04T01:50:57.725Z" )
.put( "AWSAccessKeyId", "7C6LEIR2VTIXHPTXTCEAD" )
.build()),
"GET", "/services/CloudWatch/", "10.111.1.116:8773",
2,
Hmac.HmacSHA256);
assertTrue("Authentication successful", hmacV2LoginModule("7pG138tHUyt5YjOd4gRLZSOX6Nn77skFg29r1SQk").authenticate(creds));
}
/**
* Amazon CLI/ElasticLoadBalancing 1.0.17.0 API 2012-06-01
*
* NOTE: Signature is invalid due to signed path being "/services/loadbalancing"
*/
@Test
public void testAWSElasticLoadBalancingCLI_1_0_17_0_Sigv2() throws Exception {
final HmacCredentials creds = creds(
"NAbXQmUTwLxyT1cqXbOi6iMX7TQt5haL9KgrhBO+zaA=",
Maps.newHashMap(ImmutableMap.<String,String>builder()
.put( "Version", "2012-06-01" )
.put( "Action", "DescribeLoadBalancers" )
.put( "SignatureVersion", "2" )
.put( "SignatureMethod", "HmacSHA256" )
.put( "Timestamp", "2013-04-04T01:54:27.366Z" )
.put( "AWSAccessKeyId", "7C6LEIR2VTIXHPTXTCEAD" )
.build()),
"GET", "/services/LoadBalancing/", "10.111.1.116:8773",
2,
Hmac.HmacSHA256);
assertTrue("Authentication successful", hmacV2LoginModule("7pG138tHUyt5YjOd4gRLZSOX6Nn77skFg29r1SQk").authenticate(creds));
}
@Test
public void testSignatureV4TestSuite() throws Exception {
final File tempZipFile = File.createTempFile( "aws4_testsuite", ".zip" );
tempZipFile.deleteOnExit();
ByteStreams.copy( Resources.asByteSource( HmacLoginModuleTest.class.getResource( "aws4_testsuite.zip" ) ).openStream( ), new FileOutputStream( tempZipFile ) );
final ZipFile testSuiteZip = new ZipFile( tempZipFile );
final Set<String> testNames = Sets.newTreeSet();
final Splitter nameSplit = Splitter.on( Pattern.compile("aws4_testsuite/|\\.[a-z]{3,5}$") );
for ( final ZipEntry entry : Collections.list( testSuiteZip.entries() ) ) {
testNames.add( Iterables.get( nameSplit.split( entry.getName() ), 1, "" ) );
}
System.out.println( testNames );
assertTrue( "No tests found!", !testNames.isEmpty() );
testNames.removeAll( Lists.newArrayList( "get-vanilla-ut8-query", "get-vanilla-query-unreserved", "post-vanilla-query-nonunreserved" ) ); //invalid tests?
for ( final String testName : testNames ) {
System.out.println( testName );
final String authz = slurpTestFile( testSuiteZip, testName + ".authz" );
final String sreq = slurpTestFile( testSuiteZip, testName + ".sreq" );
if ( authz == null || sreq == null ) {
System.out.println("Skipping test with missing files: " + testName);
continue;
}
final String pathWithQuery = Iterables.get( Splitter.on(" ").limit( 3 ).split( sreq ), 1);
final HmacCredentials creds = new HmacCredentials(
"1234567890",
HmacUtils.SignatureVariant.SignatureV4Standard,
Maps.transformValues( Multimaps.index( Splitter.on( "&" ).omitEmptyStrings().split( Iterables.get( Splitter.on( "?" ).limit( 2 ).split( pathWithQuery ), 1, "" ) ), new Function<String, String>() {
@Override
public String apply( final String nvp ) {
return Iterables.get( Splitter.on( "=" ).limit( 2 ).split( nvp ), 0 );
}
} ).asMap(), new Function<Collection<String>, List<String>>() {
@Override
public List<String> apply( final Collection<String> values ) {
return Lists.transform( Lists.newArrayList( values ), new Function<String, String>() {
@Override
public String apply( final String value ) {
return Iterables.get( Splitter.on( "=" ).limit( 2 ).split( value ), 1, "" );
}
} );
}
} ),
Maps.transformValues( Multimaps.index( Iterables.filter( Splitter.on( "\r\n" ).split( sreq ), Predicates.containsPattern( ":" ) ), new Function<String, String>() {
@Override
public String apply( final String line ) {
return Iterables.get( Splitter.on( ":" ).limit( 2 ).split( line ), 0 ).toLowerCase();
}
} ).asMap(), new Function<Collection<String>, List<String>>() {
@Override
public List<String> apply( final Collection<String> values ) {
return Lists.transform( Lists.newArrayList( values ), new Function<String, String>() {
@Override
public String apply( final String value ) {
return Iterables.get( Splitter.on( ":" ).limit( 2 ).split( value ), 1 );
}
} );
}
} ),
Iterables.get( Splitter.on(" ").limit( 2 ).split( sreq ), 0),
Iterables.get( Splitter.on("?").limit( 2 ).split( pathWithQuery ), 0),
Iterables.get( Splitter.on("\r\n\r\n").limit( 2 ).split( sreq ), 1, "")
);
assertTrue("Authentication successful " + testName, hmacV4LoginModule().authenticate(creds));
}
assertTrue( "Deleted temp zip file", tempZipFile.delete() );
testSuiteZip.close();
}
/**
* AWS Java SDK version 1.3.26, signs with path of "/"
*/
@Test
public void testAWSJavaSDK_1_3_26_SigV4() throws Exception {
final HmacCredentials creds = new HmacCredentials(
"1234567890",
HmacUtils.SignatureVariant.SignatureV4Standard,
Collections.<String,List<String>>emptyMap(),
ImmutableMap.<String,List<String>>builder()
.put( "host", Lists.newArrayList( "b-28.devtest.eucalyptus-systems.com:8773" ) )
.put( "x-amz-date", Lists.newArrayList( "20130112T025140Z" ) )
.put( "authorization", Lists.newArrayList( "AWS4-HMAC-SHA256 Credential=K4DM6CICEOS4Y6IORG7I5/20130112/us-east-1/iam/aws4_request, SignedHeaders=host;user-agent;x-amz-content-sha256;x-amz-date, Signature=9dd90e072ce991059ed4fefdeff3e37317abb9f4816be301573444040ff01900" ) )
.put( "user-agent", Lists.newArrayList( "aws-sdk-java/1.3.26 Linux/3.6.10-2.fc16.x86_64 Java_HotSpot(TM)_64-Bit_Server_VM/20.6-b01" ) )
.put( "x-amz-content-sha256", Lists.newArrayList( "5f776d91509b9c99b8cb5eb5d6d4a787a33ae41c8cd6e7b69effca69080e1e1f" ) )
.put( "content-type", Lists.newArrayList( "application/x-www-form-urlencoded; charset=utf-8" ) )
.put( "content-length", Lists.newArrayList( "36" ) )
.build(),
"POST",
"/services/Euare/",
"Action=ListGroups&Version=2010-05-08"
);
assertTrue("Authentication successful", hmacV4LoginModule("ea9nMgw6353ANsJeylVkNIIzuCU0hz0xtErRbcj0").authenticate(creds));
}
@Test
public void testBoto_2_27_0_SigV4() throws Exception {
final HmacCredentials creds = new HmacCredentials(
"1234567890",
HmacUtils.SignatureVariant.SignatureV4Standard,
ImmutableMap.<String,List<String>>builder()
.put( "Action", Lists.newArrayList( "ListMetrics" ) )
.put( "Version", Lists.newArrayList( "2010-08-01" ) )
.build(),
ImmutableMap.<String,List<String>>builder()
.put( "host", Lists.newArrayList( "cloudwatch.g-12-03.autoqa.qa1.eucalyptus-systems.com:8773" ) )
.put( "x-amz-date", Lists.newArrayList( "20140507T202657Z" ) )
.put( "authorization", Lists.newArrayList( "AWS4-HMAC-SHA256 Credential=AKI67CCVBS1XAL7UG9KE/20140507/g-12-03/cloudwatch/aws4_request, SignedHeaders=host;x-amz-date, Signature=1a74acf1bf2b22bddad7314cc44eef6fe562a50373ca239113d2c1942a677739" ) )
.build(),
"GET",
"/",
""
);
assertTrue("Authentication successful", hmacV4LoginModule("vNhDy9ERZQP5WXCdmPR7ZbbzZwdlQXETeZ6wM64i").authenticate(creds));
}
/**
* EUCA-4748 sig v2
*/
@Test
public void testSpecialCharacters_Sigv2() throws Exception {
final HmacCredentials creds = creds(
"J8+AGPWqWNL8n7n/rjvyb+vQlKtiF+Bl4e/1rO/bUHU=",
Maps.newHashMap(ImmutableMap.<String,String>builder()
.put( "Version", "2013-02-01" )
.put( "Action", "CreateSecurityGroup" )
.put( "GroupDescription", "!@$$%^^&&*()*&&^%{}\":?><|][~" )
.put( "GroupName", "group1" )
.put( "SignatureVersion", "2" )
.put( "SignatureMethod", "HmacSHA256" )
.put( "Timestamp", "2013-06-17T21:55:32Z" )
.put( "AWSAccessKeyId", "YHZWRDQW0LZMFRPWWHS6F" )
.build()),
"POST", "/services/Eucalyptus", "10.20.10.61:8773",
2,
Hmac.HmacSHA256);
assertTrue("Authentication successful", hmacV2LoginModule("K8VKG93wNXV9i3P6hgSUPMiK40CAfBMmntBCb4bs").authenticate(creds));
}
private TestHmacLoginModule loginModule() {
return new TestHmacLoginModule();
}
private static class TestHmacLoginModule extends HmacLoginModuleSupport {
private TestHmacLoginModule() {
super(-1);
}
@Override
public boolean authenticate( final HmacCredentials credentials ) throws Exception {
throw new Exception("Not implemented");
}
}
private String slurpTestFile( final ZipFile testSuiteZip, final String testName ) throws IOException {
final ZipEntry entry = testSuiteZip.getEntry( "aws4_testsuite/" + testName );
final byte[] data = entry == null ? null : ByteStreams.toByteArray( testSuiteZip.getInputStream( entry ) );
return data == null ? null : new String(data, Charsets.UTF_8);
}
private HmacCredentials creds( final String signature,
final Map<String,String> parameters,
final String verb,
final String servicePath,
final String headerHost,
final Integer signatureVersion,
final Hmac hmacType ) throws AuthenticationException {
final Map<String,List<String>> paramMap = Maps.newHashMap( Maps.transformValues(parameters, CollectionUtils.<String>listUnit()) );
paramMap.put( "Signature", Collections.singletonList( signature ) );
final HmacCredentials creds = new HmacCredentials(
"1234567890",
signatureVersion == 1 ? HmacUtils.SignatureVariant.SignatureV1Standard : HmacUtils.SignatureVariant.SignatureV2Standard,
paramMap,
Collections.singletonMap( "host", Collections.singletonList(headerHost) ),
verb,
servicePath,
"" );
if ( signatureVersion == 1 || ( signatureVersion == 2 && !paramMap.containsKey( SecurityParameter.SignatureMethod.parameter() ) ) ) {
creds.setSignatureMethod( hmacType );
}
return creds;
}
//TODO:MOCKING
private Hmacv2LoginModule hmacV2LoginModule() {
return hmacV2LoginModule( "ZRvYnXG04PxhYuP228IWLmCG0o3kYIr2fPByxMlb" );
}
private Hmacv2LoginModule hmacV2LoginModule( final String secret ) {
return new Hmacv2LoginModule(){
@Override
protected AccessKey lookupAccessKey(final HmacCredentials credentials) throws AuthException {
return accessKey( secret );
}
};
}
private Hmacv4LoginModule hmacV4LoginModule( ) {
return hmacV4LoginModule( "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY" );
}
private Hmacv4LoginModule hmacV4LoginModule( final String secret ) {
return new Hmacv4LoginModule(){
@Override
protected AccessKey lookupAccessKey( final HmacCredentials credentials ) throws AuthException {
return accessKey( secret );
}
};
}
private AccessKey accessKey() {
return accessKey( "ZRvYnXG04PxhYuP228IWLmCG0o3kYIr2fPByxMlb" );
}
private AccessKey accessKey( final String secretKey ) {
return new AccessKey() {
private static final long serialVersionUID = 1L;
@Override
public Boolean isActive() {
return true;
}
@Override
public String getAccessKey() {
throw new IllegalStateException();
}
@Override
public String getSecretKey() {
return secretKey;
}
@Override
public Date getCreateDate() {
throw new IllegalStateException();
}
@Override
public UserPrincipal getPrincipal() throws AuthException {
return null;
}
};
}
}