Java tutorial
/* * Copyright 2014-2016 ksyun.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://ksyun.com/apache2.0 * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ package com.ksc.auth.profile.internal; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Scanner; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.ksc.KscClientException; import com.ksc.auth.AWSCredentials; import com.ksc.auth.BasicAWSCredentials; import com.ksc.auth.BasicSessionCredentials; import com.ksc.auth.profile.internal.securitytoken.ProfileCredentialsService; import com.ksc.auth.profile.internal.securitytoken.RoleInfo; import com.ksc.util.StringUtils; public class ProfilesConfigFileLoader { private static final Log LOG = LogFactory.getLog(ProfilesConfigFileLoader.class); public static Map<String, Profile> loadProfiles(File file, ProfileCredentialsService profileCredentialsService) { if (file == null) { throw new IllegalArgumentException("Unable to load AWS profiles: specified file is null."); } if (!file.exists() || !file.isFile()) { throw new IllegalArgumentException( "AWS credential profiles file not found in the given path: " + file.getAbsolutePath()); } FileInputStream fis = null; try { fis = new FileInputStream(file); return loadProfiles(fis, profileCredentialsService); } catch (IOException ioe) { throw new KscClientException( "Unable to load AWS credential profiles file at: " + file.getAbsolutePath(), ioe); } finally { if (fis != null) try { fis.close(); } catch (IOException ioe) { } } } /** * Loads the credential profiles from the given input stream. * * @param is input stream from where the profile details are read. * @throws IOException */ private static Map<String, Profile> loadProfiles(InputStream is, ProfileCredentialsService profileCredentialsService) throws IOException { ProfilesConfigFileLoaderHelper helper = new ProfilesConfigFileLoaderHelper(); Map<String, Map<String, String>> allProfileProperties = helper .parseProfileProperties(new Scanner(is, StringUtils.UTF8.name())); // Convert the loaded property map to credential objects Map<String, Profile> profilesByName = new LinkedHashMap<String, Profile>(); for (Entry<String, Map<String, String>> entry : allProfileProperties.entrySet()) { String profileName = entry.getKey(); Map<String, String> properties = entry.getValue(); if (profileName.startsWith("profile ")) { LOG.warn("The legacy profile format requires the 'profile ' prefix before the profile name. " + "The latest code does not require such prefix, and will consider it as part of the profile name. " + "Please remove the prefix if you are seeing this warning."); } assertParameterNotEmpty(profileName, "Unable to load credentials into profile: ProfileName is empty."); if (properties.containsKey(Profile.ROLE_ARN)) { profilesByName.put(profileName, fromAssumeRole(profileName, properties, allProfileProperties, profileCredentialsService)); } else { profilesByName.put(profileName, fromStaticCredentials(profileName, properties)); } } return profilesByName; } private static Profile fromStaticCredentials(String profileName, Map<String, String> properties) { String accessKey = properties.get(Profile.AWS_ACCESS_KEY_ID); String secretKey = properties.get(Profile.AWS_SECRET_ACCESS_KEY); String sessionToken = properties.get(Profile.AWS_SESSION_TOKEN); if (StringUtils.isNullOrEmpty(accessKey)) { throw new KscClientException(String.format( "Unable to load credentials into profile [%s]: AWS Access Key ID is not specified.", profileName)); } if (StringUtils.isNullOrEmpty(secretKey)) { throw new KscClientException(String.format( "Unable to load credentials into profile [%s]: AWS Secret Access Key is not specified.", profileName)); } if (sessionToken == null) { return new Profile(profileName, new BasicAWSCredentials(accessKey, secretKey)); } else { if (sessionToken.isEmpty()) { throw new KscClientException(String.format( "Unable to load credentials into profile [%s]: AWS Session Token is empty.", profileName)); } return new Profile(profileName, new BasicSessionCredentials(accessKey, secretKey, sessionToken)); } } private static Profile fromAssumeRole(String profileName, Map<String, String> properties, Map<String, Map<String, String>> allProfileProperties, ProfileCredentialsService profileCredentialsService) { String roleArn = properties.get(Profile.ROLE_ARN); String sourceProfileName = properties.get(Profile.SOURCE_PROFILE); String roleSessionName = properties.get(Profile.ROLE_SESSION_NAME); String externalId = properties.get(Profile.EXTERNAL_ID); if (StringUtils.isNullOrEmpty(sourceProfileName)) { return Profile.createInvalidProfile(profileName, String.format( "Unable to load credentials into profile [%s]: Source profile name is not specified", profileName)); } Map<String, String> sourceProfileProperties = allProfileProperties.get(sourceProfileName); if (sourceProfileProperties == null) { return Profile.createInvalidProfile(profileName, String.format("Unable to load source profile [%s]: Source profile not found [%s]", profileName, sourceProfileName)); } String sourceAccessKey = sourceProfileProperties.get(Profile.AWS_ACCESS_KEY_ID); String sourceSecretKey = sourceProfileProperties.get(Profile.AWS_SECRET_ACCESS_KEY); String sourceSessionToken = sourceProfileProperties.get(Profile.AWS_SESSION_TOKEN); AWSCredentials sourceCredentials; if (StringUtils.isNullOrEmpty(sourceAccessKey)) { return Profile.createInvalidProfile(profileName, String.format( "Unable to load credentials into profile [%s]: AWS Access Key ID is not specified in source profile [%s].", profileName, sourceProfileName)); } if (StringUtils.isNullOrEmpty(sourceSecretKey)) { return Profile.createInvalidProfile(profileName, String.format( "Unable to load credentials into profile [%s]: AWS Secret Access Key is not specified in source profile [%s].", profileName, sourceProfileName)); } if (StringUtils.isNullOrEmpty(sourceSessionToken)) { sourceCredentials = new BasicAWSCredentials(sourceAccessKey, sourceSecretKey); } else { sourceCredentials = new BasicSessionCredentials(sourceAccessKey, sourceSecretKey, sourceSessionToken); } if (StringUtils.isNullOrEmpty(roleSessionName)) { roleSessionName = "aws-sdk-java-" + System.currentTimeMillis(); } RoleInfo roleInfo = new RoleInfo().withRoleArn(roleArn).withRoleSessionName(roleSessionName) .withExternalId(externalId).withLongLivedCredentials(sourceCredentials); return new Profile(profileName, sourceProfileName, profileCredentialsService.getAssumeRoleCredentialsProvider(roleInfo), roleInfo); } /** * <p> * Asserts that the specified parameter value is neither <code>empty</code> * nor null, and if it is, throws an <code>AmazonClientException</code> with * the specified error message. * </p> * * @param parameterValue * The parameter value being checked. * @param errorMessage * The error message to include in the AmazonClientException if * the specified parameter value is empty. */ private static void assertParameterNotEmpty(String parameterValue, String errorMessage) { if (StringUtils.isNullOrEmpty(parameterValue)) { throw new KscClientException(errorMessage); } } /** * Implementation of AbstractProfilesConfigFileScanner that groups profile * properties into a map while scanning through the credentials profile. */ private static class ProfilesConfigFileLoaderHelper extends AbstractProfilesConfigFileScanner { /** * Map from the parsed profile name to the map of all the property values * included the specific profile */ protected final Map<String, Map<String, String>> allProfileProperties = new LinkedHashMap<String, Map<String, String>>(); /** * Parses the input and returns a map of all the profile properties. */ public Map<String, Map<String, String>> parseProfileProperties(Scanner scanner) { allProfileProperties.clear(); run(scanner); return new LinkedHashMap<String, Map<String, String>>(allProfileProperties); } @Override protected void onEmptyOrCommentLine(String profileName, String line) { // Ignore empty or comment line } @Override protected void onProfileStartingLine(String newProfileName, String line) { // If the same profile name has already been declared, clobber the // previous one allProfileProperties.put(newProfileName, new HashMap<String, String>()); } @Override protected void onProfileEndingLine(String prevProfileName) { // No-op } @Override protected void onProfileProperty(String profileName, String propertyKey, String propertyValue, boolean isSupportedProperty, String line) { if (!isSupportedProperty) { LOG.info(String.format("Skip unsupported property name %s in profile [%s].", propertyKey, profileName)); return; } // Not strictly necessary, since the abstract super class guarantees // onProfileStartingLine is always invoked before this method. // Just to be safe... if (allProfileProperties.get(profileName) == null) { allProfileProperties.put(profileName, new HashMap<String, String>()); } Map<String, String> properties = allProfileProperties.get(profileName); if (properties.containsKey(propertyKey)) { throw new IllegalArgumentException("Duplicate property values for [" + propertyKey + "]."); } properties.put(propertyKey, propertyValue); } @Override protected void onEndOfFile() { // No-op } } }