/* * Copyright 2010-2016 Amazon.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://aws.amazon.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.amazonaws.auth.policy.internal; import com.amazonaws.auth.policy.Action; import com.amazonaws.auth.policy.Condition; import com.amazonaws.auth.policy.Policy; import com.amazonaws.auth.policy.Principal; import com.amazonaws.auth.policy.Resource; import com.amazonaws.auth.policy.Statement; import com.amazonaws.util.json.AwsJsonWriter; import com.amazonaws.util.json.JsonUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * Serializes an AWS policy object to a JSON string, suitable for sending to an * AWS service. */ public class JsonPolicyWriter { /** The JSON Generator to generator a JSON string. */ private AwsJsonWriter jsonWriter = null; /** The output writer to which the JSON String is written. */ private final Writer writer; /** Logger used to log exceptions that occurs while writing the JSON policy. */ private static final Log log = LogFactory.getLog("com.amazonaws.auth.policy"); /** * Constructs a new instance of JSONPolicyWriter. */ public JsonPolicyWriter() { writer = new StringWriter(); jsonWriter = JsonUtils.getJsonWriter(writer); } /** * Converts the specified AWS policy object to a JSON string, suitable for * passing to an AWS service. * * @param policy The AWS policy object to convert to a JSON string. * @return The JSON string representation of the specified policy object. * @throws IllegalArgumentException If the specified policy is null or * invalid and cannot be serialized to a JSON string. */ public String writePolicyToString(Policy policy) { if (!isNotNull(policy)) throw new IllegalArgumentException("Policy cannot be null"); try { return jsonStringOf(policy); } catch (Exception e) { String message = "Unable to serialize policy to JSON string: " + e.getMessage(); throw new IllegalArgumentException(message, e); } finally { try { writer.close(); } catch (Exception e) { } } } /** * Converts the given <code>Policy</code> into a JSON String. * * @param policy the policy to be converted. * @return a JSON String of the specified policy object. */ private String jsonStringOf(Policy policy) throws IOException { jsonWriter.beginObject(); writeJsonKeyValue(JsonDocumentFields.VERSION, policy.getVersion()); if (isNotNull(policy.getId())) writeJsonKeyValue(JsonDocumentFields.POLICY_ID, policy.getId()); writeJsonArrayStart(JsonDocumentFields.STATEMENT); for (Statement statement : policy.getStatements()) { jsonWriter.beginObject(); if (isNotNull(statement.getId())) { writeJsonKeyValue(JsonDocumentFields.STATEMENT_ID, statement.getId()); } writeJsonKeyValue(JsonDocumentFields.STATEMENT_EFFECT, statement .getEffect().toString()); List<Principal> principals = statement.getPrincipals(); if (isNotNull(principals) && !principals.isEmpty()) writePrincipals(principals); List<Action> actions = statement.getActions(); if (isNotNull(actions) && !actions.isEmpty()) writeActions(actions); List<Resource> resources = statement.getResources(); if (isNotNull(resources) && !resources.isEmpty()) writeResources(resources); List<Condition> conditions = statement.getConditions(); if (isNotNull(conditions) && !conditions.isEmpty()) writeConditions(conditions); jsonWriter.endObject(); } writeJsonArrayEnd(); jsonWriter.endObject(); jsonWriter.flush(); return writer.toString(); } /** * Writes the list of conditions to the JSONGenerator. * * @param conditions the conditions to be written. */ private void writeConditions(List<Condition> conditions) throws IOException { Map<String, ConditionsByKey> conditionsByType = groupConditionsByTypeAndKey(conditions); writeJsonObjectStart(JsonDocumentFields.CONDITION); ConditionsByKey conditionsByKey; for (Map.Entry<String, ConditionsByKey> entry : conditionsByType .entrySet()) { conditionsByKey = conditionsByType.get(entry.getKey()); writeJsonObjectStart(entry.getKey()); for (String key : conditionsByKey.keySet()) { writeJsonArray(key, conditionsByKey.getConditionsByKey(key)); } writeJsonObjectEnd(); } writeJsonObjectEnd(); } /** * Writes the list of <code>Resource</code>s to the JSONGenerator. * * @param resources the list of resources to be written. */ private void writeResources(List<Resource> resources) throws IOException { List<String> resourceStrings = new ArrayList<String>(); for (Resource resource : resources) { resourceStrings.add(resource.getId()); } writeJsonArray(JsonDocumentFields.RESOURCE, resourceStrings); } /** * Writes the list of <code>Action</code>s to the JSONGenerator. * * @param actions the list of the actions to be written. */ private void writeActions(List<Action> actions) throws IOException { List<String> actionStrings = new ArrayList<String>(); for (Action action : actions) { actionStrings.add(action.getActionName()); } writeJsonArray(JsonDocumentFields.ACTION, actionStrings); } /** * Writes the list of <code>Principal</code>s to the JSONGenerator. * * @param principals the list of principals to be written. */ private void writePrincipals(List<Principal> principals) throws IOException { if (principals.size() == 1 && principals.get(0).equals(Principal.All)) { writeJsonKeyValue(JsonDocumentFields.PRINCIPAL, Principal.All.getId()); } else { writeJsonObjectStart(JsonDocumentFields.PRINCIPAL); Map<String, List<String>> principalsByScheme = groupPrincipalByScheme(principals); List<String> principalValues; for (Map.Entry<String, List<String>> entry : principalsByScheme.entrySet()) { principalValues = principalsByScheme.get(entry.getKey()); if (principalValues.size() == 1) { writeJsonKeyValue(entry.getKey(), principalValues.get(0)); } else { writeJsonArray(entry.getKey(), principalValues); } } writeJsonObjectEnd(); } } /** * Groups the list of <code>Principal</code>s by the Scheme. * * @param principals the list of <code>Principal</code>s * @return a map grouped by scheme of the principal. */ private Map<String, List<String>> groupPrincipalByScheme( List<Principal> principals) { Map<String, List<String>> principalsByScheme = new HashMap<String, List<String>>(); String provider; List<String> principalValues; for (Principal principal : principals) { provider = principal.getProvider(); if (!principalsByScheme.containsKey(provider)) { principalsByScheme.put(provider, new ArrayList<String>()); } principalValues = principalsByScheme.get(provider); principalValues.add(principal.getId()); } return principalsByScheme; } /** * Inner class to hold condition values for each key under a condition type. */ static class ConditionsByKey { private Map<String, List<String>> conditionsByKey; public ConditionsByKey() { conditionsByKey = new HashMap<String, List<String>>(); } public Map<String, List<String>> getConditionsByKey() { return conditionsByKey; } public void setConditionsByKey(Map<String, List<String>> conditionsByKey) { this.conditionsByKey = conditionsByKey; } public boolean containsKey(String key) { return conditionsByKey.containsKey(key); } public List<String> getConditionsByKey(String key) { return conditionsByKey.get(key); } public Set<String> keySet() { return conditionsByKey.keySet(); } public void addValuesToKey(String key, List<String> values) { List<String> conditionValues = getConditionsByKey(key); if (conditionValues == null) conditionsByKey.put(key, new ArrayList<String>(values)); else conditionValues.addAll(values); } } /** * Groups the list of <code>Condition</code>s by the condition type and * condition key. * * @param conditions the list of conditions to be grouped * @return a map of conditions grouped by type and then key. */ private Map<String, ConditionsByKey> groupConditionsByTypeAndKey( List<Condition> conditions) { Map<String, ConditionsByKey> conditionsByType = new HashMap<String, ConditionsByKey>(); String type; String key; ConditionsByKey conditionsByKey; for (Condition condition : conditions) { type = condition.getType(); key = condition.getConditionKey(); if (!(conditionsByType.containsKey(type))) { conditionsByType.put(type, new ConditionsByKey()); } conditionsByKey = conditionsByType.get(type); conditionsByKey.addValuesToKey(key, condition.getValues()); } return conditionsByType; } /** * Writes an array along with its values to the JSONGenerator. * * @param arrayName name of the JSON array. * @param values values of the JSON array. */ private void writeJsonArray(String arrayName, List<String> values) throws IOException { writeJsonArrayStart(arrayName); for (String value : values) jsonWriter.value(value); writeJsonArrayEnd(); } /** * Writes the Start of Object String to the JSONGenerator along with Object * Name. * * @param fieldName name of the JSON Object. */ private void writeJsonObjectStart(String fieldName) throws IOException { jsonWriter.name(fieldName); jsonWriter.beginObject(); } /** * Writes the End of Object String to the JSONGenerator. */ private void writeJsonObjectEnd() throws IOException { jsonWriter.endObject(); } /** * Writes the Start of Array String to the JSONGenerator along with Array * Name. * * @param fieldName name of the JSON array */ private void writeJsonArrayStart(String fieldName) throws IOException { jsonWriter.name(fieldName); jsonWriter.beginArray(); } /** * Writes the End of Array String to the JSONGenerator. */ private void writeJsonArrayEnd() throws IOException { jsonWriter.endArray(); } /** * Writes the given field and the value to the JsonGenerator * * @param fieldName the JSON field name * @param value value for the field */ private void writeJsonKeyValue(String fieldName, String value) throws IOException { jsonWriter.name(fieldName); jsonWriter.value(value); } /** * Checks if the given object is not null. * * @param object the object compared to null. * @return true if the object is not null else false */ private boolean isNotNull(Object object) { return null != object; } }