Merge "b/163140877 Add schema validation for x-security-rules extension"
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtensionSchemaValidator.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtensionSchemaValidator.java
index 2372979..f4eb1c5 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtensionSchemaValidator.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtensionSchemaValidator.java
@@ -27,6 +27,8 @@
       Resources.getResource("ListWithStringsSchema.json");
   private static final URL SECURITY_DEFINITIONS_SCHEMA_URL =
       Resources.getResource("SecurityDefinitionsSchema.json");
+  private static final URL SECURITY_RULES_SCHEMA_URL =
+      Resources.getResource("SecurityRulesSchema.json");
 
   private final JsonSchemaFactory jsonSchemaFactory;
 
@@ -56,6 +58,8 @@
         case X_SECURITY_TYPE_DEFINITIONS:
           errors = validateExtensionContent(SECURITY_DEFINITIONS_SCHEMA_URL, extension);
           break;
+        case X_SECURITY_RULES:
+          errors = validateExtensionContent(SECURITY_RULES_SCHEMA_URL, extension);
       }
     } else {
       errors = defaultErrors(extension);
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionName.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionName.java
index 98eaa6b..488d068 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionName.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtensionName.java
@@ -7,7 +7,8 @@
 enum ExtensionName {
   X_SECURITY_TYPE("x-security-type"),
   X_SECURITY_ALLOW("x-security-allow"),
-  X_SECURITY_TYPE_DEFINITIONS("x-security-type-definitions");
+  X_SECURITY_TYPE_DEFINITIONS("x-security-type-definitions"),
+  X_SECURITY_RULES("x-security-rules");
 
   private final String extensionName;
 
diff --git a/oas-core/src/main/resources/SecurityRulesSchema.json b/oas-core/src/main/resources/SecurityRulesSchema.json
new file mode 100644
index 0000000..16f8dfa
--- /dev/null
+++ b/oas-core/src/main/resources/SecurityRulesSchema.json
@@ -0,0 +1,159 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "Security rules schema",
+  "type": "array",
+  "uniqueItems": true,
+  "items": {
+    "type": "object",
+    "properties": {
+      "type": {
+        "type": "string"
+      },
+      "rule": {
+        "type": "string"
+      },
+      "configuration": {
+        "type": "array",
+        "uniqueItems": true,
+        "items": {
+          "type": "object",
+          "properties": {
+            "parameter": {
+              "type": "string"
+            },
+            "value": {
+              "anyOf": [
+                {
+                  "type": ["string", "number", "boolean"]
+                },
+                {
+                  "type": "array",
+                  "items": {
+                    "type": ["string", "number", "boolean"]
+                  }
+                }
+              ]
+            }
+          }
+        },
+        "required": [
+          "parameter"
+        ]
+      }
+    },
+    "required": [
+      "type", "rule"
+    ],
+    "additionalProperties": false
+  },
+  "tests": [
+    {
+      "id": "VALID_1",
+      "description": "type, rule, and multiple configurations with array, string, number, and boolean value types",
+      "data": [
+        {
+          "type": "customType",
+          "rule": "customRule",
+          "configuration": [
+            {
+              "parameter": "customParam1",
+              "value": ["customValue1", 1, true]
+            },
+            {
+              "parameter": "customParam2",
+              "value": true
+            }
+          ]
+        }
+      ],
+      "valid": true
+    },
+    {
+      "id": "VALID_2",
+      "description": "configuration without value",
+      "data": [
+        {
+          "type": "customType",
+          "rule": "customRule",
+          "configuration": [
+            {
+              "parameter": "customParam1"
+            }
+          ]
+        }
+      ],
+      "valid": true
+    },
+    {
+      "id": "VALID_3",
+      "description": "type and rule without configuration",
+      "data": [
+        {
+          "type": "customType",
+          "rule": "customRule"
+        }
+      ],
+      "valid": true
+    },
+    {
+      "id": "VALID_4",
+      "$comment": "This test should be INVALID but JSON Schema doesn't allow individual field uniqueness check(https://github.com/json-schema-org/json-schema-vocabularies/issues/22)",
+      "description": "unique type field check",
+      "data": [
+        {
+          "type": "customType",
+          "rule": "customRule1"
+        },
+        {
+          "type": "customType",
+          "rule": "customRule2"
+        }
+      ],
+      "valid": true
+    },
+    {
+      "id": "INVALID_1",
+      "description": "unique items check",
+      "data": [
+        {
+          "type": "customType",
+          "rule": "customRule"
+        },
+        {
+          "type": "customType",
+          "rule": "customRule"
+        }
+      ],
+      "valid": false
+    },
+    {
+      "id": "INVALID_2",
+      "description": "type and rule missing",
+      "data": [
+        {}
+      ],
+      "valid": false
+    },
+    {
+      "id": "INVALID_3",
+      "description": "null configuration value check",
+      "data": [
+        {
+          "type": "customType",
+          "rule": "customRule",
+          "configuration": [
+            {
+              "parameter": "customParam",
+              "value": null
+            }
+          ]
+        },
+        {
+          "type": "customType",
+          "rule": "customRule"
+        }
+      ],
+      "valid": false
+    }
+  ]
+}
\ No newline at end of file
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseExtensionSchemaValidatorTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseExtensionSchemaValidatorTest.java
index b4e1e61..7112286 100644
--- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseExtensionSchemaValidatorTest.java
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseExtensionSchemaValidatorTest.java
@@ -1,5 +1,6 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import static com.apigee.security.oas.extendedvalidator.ExtensionName.X_SECURITY_RULES;
 import static com.apigee.security.oas.extendedvalidator.ExtensionName.X_SECURITY_TYPE;
 import static com.apigee.security.oas.extendedvalidator.ExtensionName.X_SECURITY_TYPE_DEFINITIONS;
 import static com.google.common.collect.Iterables.concat;
@@ -50,6 +51,8 @@
       Resources.getResource("ListWithStringsSchema.json");
   private static final URL SECURITY_DEFINITIONS_SCHEMA_URL =
       Resources.getResource("SecurityDefinitionsSchema.json");
+  private static final URL SECURITY_RULES_SCHEMA_URL =
+      Resources.getResource("SecurityRulesSchema.json");
   private static final JsonNode emptyContent = new ObjectMapper().valueToTree("[]");
   private static final ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>>
       extensionPath =
@@ -99,20 +102,19 @@
     }
 
     /** Builds global test parameters for multiple schemas. */
-    @Parameters(name = "{index}: extension: {0}, isValid: {1}, Data: {2}")
+    @Parameters(name = "{index}: extension: {0}, Data: {1}, isValid: {2}")
     public static Collection<Object[]> data() throws IOException {
 
-      Collection<Object[]> arrayListSchemaTestParameters =
-          buildTestParameters(
-              ARRAY_LIST_SECURITY_TYPES_SCHEMA_URL, X_SECURITY_TYPE.getExtensionName());
-      Collection<Object[]> securityDefinitionSchemaTestParameters =
-          buildTestParameters(
-              SECURITY_DEFINITIONS_SCHEMA_URL, X_SECURITY_TYPE_DEFINITIONS.getExtensionName());
-
-      Iterable<Object[]> combinedIterables =
+      return Lists.newArrayList(
           unmodifiableIterable(
-              concat(arrayListSchemaTestParameters, securityDefinitionSchemaTestParameters));
-      return Lists.newArrayList(combinedIterables);
+              concat(
+                  buildTestParameters(
+                      ARRAY_LIST_SECURITY_TYPES_SCHEMA_URL, X_SECURITY_TYPE.getExtensionName()),
+                  buildTestParameters(
+                      SECURITY_DEFINITIONS_SCHEMA_URL,
+                      X_SECURITY_TYPE_DEFINITIONS.getExtensionName()),
+                  buildTestParameters(
+                      SECURITY_RULES_SCHEMA_URL, X_SECURITY_RULES.getExtensionName()))));
     }
 
     @Test