in src/PSRule.Rules.Azure/Data/Policy/PolicyAssignmentVisitor.cs [443:556]
internal void ExpandPolicyRule(JToken policyRule, IList<string> types)
{
if (policyRule.Type == JTokenType.Object)
{
var hasFieldType = false;
var hasFieldCount = false;
// Go through each property and make sure fields and counts are sorted first
foreach (var child in policyRule.Children<JProperty>().OrderBy(prop => prop, new PropertyNameComparer()))
{
// Expand field aliases
if (child.TryGetProperty(PROPERTY_FIELD, out var field))
{
if (field.Equals(PROPERTY_TYPE, StringComparison.OrdinalIgnoreCase))
{
hasFieldType = true;
child.Parent[PROPERTY_TYPE] = DOT;
child.Remove();
}
if (TryPolicyAliasPath(field, out var aliasPath))
policyRule[child.Name] = aliasPath;
}
// Set policy rule type
else if (hasFieldType && child.TryGetProperty(PROPERTY_EQUALS, out field))
{
if (string.Equals(TYPE_SUBSCRIPTION_RESOURCE_GROUP, field, StringComparison.OrdinalIgnoreCase))
field = TYPE_RESOURCE_GROUP;
types.Add(field);
SetDefaultResourceType(field);
}
// Replace equals with count if field count expression is currently being visited
// Replace notEquals with notCount if field count expression is currently being visited
else if (hasFieldCount && (child.TryRenameProperty(PROPERTY_EQUALS, PROPERTY_COUNT) ||
child.TryRenameProperty(PROPERTY_NOTEQUALS, PROPERTY_NOTCOUNT)))
{
// Do nothing.
}
// Expand field count expressions
else if (child.Name.Equals(PROPERTY_COUNT, StringComparison.OrdinalIgnoreCase))
{
hasFieldCount = true;
if (child.Value.Type == JTokenType.Object)
{
var countObject = child.Value.ToObject<JObject>();
if (countObject.TryStringProperty(PROPERTY_FIELD, out var outerFieldAlias) &&
TryPolicyAliasPath(outerFieldAlias, out var outerFieldAliasPath))
{
if (countObject.TryObjectProperty(PROPERTY_WHERE, out var whereExpression))
{
// field in where expression
var fieldFilter = GetFieldObjectPathArrayFilter(whereExpression);
if (fieldFilter != null)
{
var splitAliasPath = outerFieldAliasPath.SplitByLastSubstring(COLLECTION_ALIAS);
policyRule[PROPERTY_FIELD] = FormatObjectPathArrayExpression(splitAliasPath[0], fieldFilter);
}
// nested allOf in where expression
else if (whereExpression.TryArrayProperty(PROPERTY_ALLOF, out var allofExpression))
{
var splitAliasPath = outerFieldAliasPath.SplitByLastSubstring(COLLECTION_ALIAS);
var filter = new StringBuilder();
ExpressionToObjectPathArrayFilter(allofExpression, AND_CLAUSE, filter);
policyRule[PROPERTY_FIELD] = FormatObjectPathArrayExpression(splitAliasPath[0], filter.ToString());
}
// nested anyOf in where expression
else if (whereExpression.TryArrayProperty(PROPERTY_ANYOF, out var anyOfExpression))
{
var splitAliasPath = outerFieldAliasPath.SplitByLastSubstring(COLLECTION_ALIAS);
var filter = new StringBuilder();
ExpressionToObjectPathArrayFilter(anyOfExpression, OR_CLAUSE, filter);
policyRule[PROPERTY_FIELD] = FormatObjectPathArrayExpression(splitAliasPath[0], filter.ToString());
}
}
// Single field in count expression
else
policyRule[PROPERTY_FIELD] = outerFieldAliasPath;
// Remove the count property when we're done
policyRule[PROPERTY_COUNT].Parent.Remove();
}
}
}
// Convert string booleans for exists expression
else if (child.Name.Equals(PROPERTY_EXISTS, StringComparison.OrdinalIgnoreCase) && child.Value.Type == JTokenType.String)
policyRule[child.Name] = child.Value.Value<bool>();
// Expand string expressions
else if (child.Value.Type == JTokenType.String)
{
var expression = GetExpression(child);
policyRule[child.Name] = expression;
}
// Recurse any objects or arrays
else if (child.Value.Type is JTokenType.Object or JTokenType.Array)
ExpandPolicyRule(child.Value, types);
}
}
// Recurse arrays
else if (policyRule.Type == JTokenType.Array)
foreach (var child in policyRule.Children().ToArray())
ExpandPolicyRule(child, types);
}