/*
* Copyright 2010-2013 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.
*/
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using ThirdParty.Json.LitJson;
using System.Globalization;
namespace Amazon.Auth.AccessControlPolicy.Internal
{
///
/// Serializes an AWS policy object to a JSON string, suitable for sending to an
/// AWS service.
///
internal static class JsonPolicyWriter
{
/**
* 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 static string WritePolicyToString(bool prettyPrint, Policy policy)
{
if (policy == null)
{
throw new ArgumentNullException("policy");
}
StringWriter writer = new StringWriter(CultureInfo.InvariantCulture);
try
{
JsonWriter generator = new JsonWriter(writer);
generator.IndentValue = 4;
generator.PrettyPrint = prettyPrint;
writePolicy(policy, generator);
return writer.ToString().Trim();
}
catch (Exception e)
{
string message = "Unable to serialize policy to JSON string: " + e.Message;
throw new ArgumentException(message, e);
}
}
private static void writePolicy(Policy policy, JsonWriter generator)
{
generator.WriteObjectStart();
writePropertyValue(generator, JsonDocumentFields.VERSION, policy.Version);
if (policy.Id != null)
{
writePropertyValue(generator, JsonDocumentFields.POLICY_ID, policy.Id);
}
generator.WritePropertyName(JsonDocumentFields.STATEMENT);
generator.WriteArrayStart();
foreach (Statement statement in policy.Statements)
{
generator.WriteObjectStart();
if (statement.Id != null)
{
writePropertyValue(generator, JsonDocumentFields.STATEMENT_ID, statement.Id);
}
writePropertyValue(generator, JsonDocumentFields.STATEMENT_EFFECT, statement.Effect.ToString());
writePrincipals(statement, generator);
writeActions(statement, generator);
writeResources(statement, generator);
writeConditions(statement, generator);
generator.WriteObjectEnd();
}
generator.WriteArrayEnd();
generator.WriteObjectEnd();
}
///
/// Uses the specified generator to write the JSON data for the principals in
/// the specified policy statement.
///
private static void writePrincipals(Statement statement, JsonWriter generator)
{
IList principals = statement.Principals;
if (principals == null || principals.Count == 0) return;
generator.WritePropertyName(JsonDocumentFields.PRINCIPAL);
if (principals.Count == 1 &&
principals[0] != null &&
principals[0].Provider.Equals(Principal.ANONYMOUS_PROVIDER, StringComparison.Ordinal))
{
generator.Write("*");
return;
}
generator.WriteObjectStart();
Dictionary> principalIdsByScheme = new Dictionary>();
foreach (Principal p in principals)
{
List principalIds;
if (!principalIdsByScheme.TryGetValue(p.Provider, out principalIds))
{
principalIds = new List();
principalIdsByScheme[p.Provider] = principalIds;
}
principalIds.Add(p.Id);
}
foreach (string scheme in principalIdsByScheme.Keys)
{
generator.WritePropertyName(scheme);
if (principalIdsByScheme[scheme].Count > 1)
{
generator.WriteArrayStart();
}
foreach (string principalId in principalIdsByScheme[scheme])
{
generator.Write(principalId);
}
if (principalIdsByScheme[scheme].Count > 1)
{
generator.WriteArrayEnd();
}
}
generator.WriteObjectEnd();
}
private static void writeActions(Statement statement, JsonWriter generator)
{
IList actions = statement.Actions;
if (actions == null || actions.Count == 0)
{
return;
}
generator.WritePropertyName(JsonDocumentFields.ACTION);
if (actions.Count > 1)
{
generator.WriteArrayStart();
}
foreach (ActionIdentifier action in actions)
{
generator.Write(action.ActionName);
}
if (actions.Count > 1)
{
generator.WriteArrayEnd();
}
}
private static void writeResources(Statement statement, JsonWriter generator)
{
IList resources = statement.Resources;
if (resources == null || resources.Count == 0)
{
return;
}
generator.WritePropertyName(JsonDocumentFields.RESOURCE);
if (resources.Count > 1)
{
generator.WriteArrayStart();
}
foreach (Resource resource in resources)
{
generator.Write(resource.Id);
}
if (resources.Count > 1)
{
generator.WriteArrayEnd();
}
}
private static void writeConditions(Statement statement, JsonWriter generator)
{
IList conditions = statement.Conditions;
if (conditions == null || conditions.Count == 0)
{
return;
}
/*
* The condition values must be grouped by all the unique condition types and keys because
* the values are written out as an array per type and key.
*/
Dictionary>> conditionsByTypeAndKeys = sortConditionsByTypeAndKey(conditions);
generator.WritePropertyName(JsonDocumentFields.CONDITION);
generator.WriteObjectStart();
foreach (KeyValuePair>> typeEntry in conditionsByTypeAndKeys)
{
generator.WritePropertyName(typeEntry.Key);
generator.WriteObjectStart();
foreach (KeyValuePair> keyEntry in typeEntry.Value)
{
IList conditionValues = keyEntry.Value;
if (conditionValues.Count == 0)
continue;
generator.WritePropertyName(keyEntry.Key);
if (conditionValues.Count > 1)
{
generator.WriteArrayStart();
}
if (conditionValues != null && conditionValues.Count != 0)
{
foreach (string conditionValue in conditionValues)
{
generator.Write(conditionValue);
}
}
if (conditionValues.Count > 1)
{
generator.WriteArrayEnd();
}
}
generator.WriteObjectEnd();
}
generator.WriteObjectEnd();
}
///
/// This sorts the conditions by condition type and key with the list of values for that combination.
///
/// The list of conditions to be sorted.
///
private static Dictionary>> sortConditionsByTypeAndKey(IList conditions)
{
Dictionary>> conditionsByTypeAndKeys = new Dictionary>>();
foreach (Condition condition in conditions)
{
string conditionType = condition.Type;
string conditionKey = condition.ConditionKey;
Dictionary> keys;
if (!conditionsByTypeAndKeys.TryGetValue(conditionType, out keys))
{
keys = new Dictionary>();
conditionsByTypeAndKeys[conditionType] = keys;
}
List values;
if (!keys.TryGetValue(conditionKey, out values))
{
values = new List();
keys[conditionKey] = values;
}
if (condition.Values != null)
{
foreach (string value in condition.Values)
{
values.Add(value);
}
}
}
return conditionsByTypeAndKeys;
}
private static void writePropertyValue(JsonWriter generator, string propertyName, string value)
{
generator.WritePropertyName(propertyName);
generator.Write(value);
}
}
}