SLING-10194 : Allow property configuration for placeholder handling
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/InternalConstants.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/InternalConstants.java
index a99f32b..8954115 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/InternalConstants.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/InternalConstants.java
@@ -76,4 +76,6 @@
static final String KEY_DEFAULT = "default";
static final String KEY_MODE = "mode";
+
+ static final String KEY_PLACEHOLDER_POLICY = "placeholder-policy";
}
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/PlaceholderPolicy.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/PlaceholderPolicy.java
new file mode 100644
index 0000000..8b11aaa
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/PlaceholderPolicy.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License 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 org.apache.sling.feature.extension.apiregions.api.config;
+
+/**
+ * A policy for using placeholders in property values.
+ * @since 1.3
+ */
+public enum PlaceholderPolicy {
+
+ /** Default policy defined by the property type. */
+ DEFAULT,
+ /** Allow placeholders in the value. */
+ ALLOW,
+ /** Require a placeholder in the value. */
+ REQUIRE,
+ /** Do not allow a placeholder in the value. */
+ DENY;
+
+}
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescription.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescription.java
index 9b1d4cf..1be609f 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescription.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescription.java
@@ -76,6 +76,12 @@
private Mode mode;
/**
+ * The placeholder policy
+ * @since 1.3
+ */
+ private PlaceholderPolicy placeholderPolicy;
+
+ /**
* Create a new description
*/
public PropertyDescription() {
@@ -86,6 +92,7 @@
this.setType(PropertyType.STRING);
this.setCardinality(1);
this.setRequired(false);
+ this.setPlaceholderPolicy(PlaceholderPolicy.DEFAULT);
}
/**
@@ -164,6 +171,10 @@
if ( modeVal != null ) {
this.setMode(Mode.valueOf(modeVal.toUpperCase()));
}
+ final String policyVal = this.getString(InternalConstants.KEY_PLACEHOLDER_POLICY);
+ if ( policyVal != null ) {
+ this.setPlaceholderPolicy(PlaceholderPolicy.valueOf(policyVal.toUpperCase()));
+ }
} catch (final JsonException | IllegalArgumentException e) {
throw new IOException(e);
}
@@ -221,6 +232,9 @@
if ( this.getMode() != null ) {
objectBuilder.add(InternalConstants.KEY_MODE, this.getMode().name());
}
+ if ( this.getPlaceholderPolicy() != PlaceholderPolicy.DEFAULT ) {
+ objectBuilder.add(InternalConstants.KEY_PLACEHOLDER_POLICY, this.getPlaceholderPolicy().name());
+ }
return objectBuilder;
}
@@ -425,4 +439,22 @@
public void setMode(final Mode value) {
this.mode = value;
}
- }
+
+ /**
+ * Get the placeholder policy.
+ * @return The policy
+ * @since 1.3
+ */
+ public PlaceholderPolicy getPlaceholderPolicy() {
+ return this.placeholderPolicy;
+ }
+
+ /**
+ * Set the placeholder policy
+ * @param policy The new policy
+ * @since 1.3
+ */
+ public void setPlaceholderPolicy(final PlaceholderPolicy policy) {
+ this.placeholderPolicy = policy == null ? PlaceholderPolicy.DEFAULT : policy;
+ }
+}
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/package-info.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/package-info.java
index 6ac6f43..b8a44cf 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/package-info.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/package-info.java
@@ -17,7 +17,7 @@
* under the License.
*/
-@org.osgi.annotation.versioning.Version("1.2.0")
+@org.osgi.annotation.versioning.Version("1.3.0")
package org.apache.sling.feature.extension.apiregions.api.config;
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/PropertyValidator.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/PropertyValidator.java
index 6cfc4f1..0e3f5be 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/PropertyValidator.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/PropertyValidator.java
@@ -26,6 +26,7 @@
import org.apache.sling.feature.extension.apiregions.api.config.Mode;
import org.apache.sling.feature.extension.apiregions.api.config.Option;
+import org.apache.sling.feature.extension.apiregions.api.config.PlaceholderPolicy;
import org.apache.sling.feature.extension.apiregions.api.config.PropertyDescription;
import org.apache.sling.feature.extension.apiregions.api.config.PropertyType;
@@ -195,7 +196,10 @@
default : context.result.getErrors().add("Unable to validate value - unknown property type : " + context.description.getType());
}
validateRegex(context, value);
- validateOptions(context, value);
+ validateOptions(context, value);
+ if ( context.description.getType() != PropertyType.PASSWORD ) {
+ validatePlaceholderPolicy(context, value, false);
+ }
} else {
// placeholder is present
if ( context.description.getType() == PropertyType.PASSWORD ) {
@@ -208,6 +212,9 @@
} else {
context.result.markSkipped();
}
+ if ( context.description.getType() != PropertyType.PASSWORD ) {
+ validatePlaceholderPolicy(context, value, true);
+ }
}
} else {
setResult(context, "Null value provided for validation");
@@ -381,7 +388,7 @@
}
void validatePassword(final Context context, final Object value, final boolean hasPlaceholder) {
- if ( !hasPlaceholder ) {
+ if ( !hasPlaceholder && context.description.getPlaceholderPolicy() != PlaceholderPolicy.DENY ) {
setResult(context, "Value for a password must use a placeholder");
}
}
@@ -447,6 +454,15 @@
}
}
+ void validatePlaceholderPolicy(final Context context, final Object value, final boolean hasPlaceholder) {
+ // for policy default and allow nothing needs to be validated
+ if ( context.description.getPlaceholderPolicy() == PlaceholderPolicy.DENY && hasPlaceholder ) {
+ setResult(context, "Placeholder in value is not allowed");
+ } else if ( context.description.getPlaceholderPolicy() == PlaceholderPolicy.REQUIRE && !hasPlaceholder ) {
+ setResult(context, "Value must use a placeholder");
+ }
+ }
+
static final class Context {
public final PropertyValidationResult result = new PropertyValidationResult();
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescriptionTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescriptionTest.java
index 2e90b3b..ed5f02f 100644
--- a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescriptionTest.java
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescriptionTest.java
@@ -53,6 +53,7 @@
entity.setType(PropertyType.BYTE);
entity.setDefaultValue("default");
entity.setMode(Mode.SILENT);
+ entity.setPlaceholderPolicy(PlaceholderPolicy.ALLOW);
entity.clear();
assertTrue(entity.getAttributes().isEmpty());
assertNull(entity.getDeprecated());
@@ -70,13 +71,14 @@
assertEquals(PropertyType.STRING, entity.getType());
assertNull(entity.getDefaultValue());
assertNull(entity.getMode());
+ assertEquals(PlaceholderPolicy.DEFAULT, entity.getPlaceholderPolicy());
}
@Test public void testFromJSONObject() throws IOException {
final Extension ext = new Extension(ExtensionType.JSON, "a", ExtensionState.OPTIONAL);
ext.setJSON("{ \"type\" : \"BYTE\", \"cardinality\": 5, \"required\" : true, \"variable\" : \"var\"," +
"\"range\" : {}, \"includes\" : [\"in\"], \"excludes\" : [\"ex\"] , \"options\": [{}], \"regex\": \".\"," +
- "\"default\" : \"def\"}");
+ "\"default\" : \"def\", \"placeholder-policy\" : \"DENY\"}");
final PropertyDescription entity = new PropertyDescription();
entity.fromJSONObject(ext.getJSONStructure().asJsonObject());
@@ -92,6 +94,7 @@
assertEquals(".", entity.getRegex());
assertNotNull(entity.getRegexPattern());
assertEquals("def", entity.getDefaultValue());
+ assertEquals(PlaceholderPolicy.DENY, entity.getPlaceholderPolicy());
// test defaults and empty values
ext.setJSON("{ \"variable\" : \"var\", \"regex\": \".\"}");
@@ -107,6 +110,7 @@
assertNull(entity.getOptions());
assertEquals(".", entity.getRegex());
assertNotNull(entity.getRegexPattern());
+ assertEquals(PlaceholderPolicy.DEFAULT, entity.getPlaceholderPolicy());
}
@Test public void testToJSONObject() throws IOException {
@@ -121,11 +125,12 @@
entity.setVariable("var");
entity.setType(PropertyType.BYTE);
entity.setDefaultValue("def");
+ entity.setPlaceholderPolicy(PlaceholderPolicy.DENY);
final Extension ext = new Extension(ExtensionType.JSON, "a", ExtensionState.OPTIONAL);
ext.setJSON("{ \"type\" : \"BYTE\", \"cardinality\": 5, \"required\" : true, \"variable\" : \"var\"," +
"\"range\" : {}, \"includes\" : [\"in\"], \"excludes\" : [\"ex\"] , \"options\": [{}], \"regex\": \".\"," +
- "\"default\" : \"def\"}");
+ "\"default\" : \"def\", \"placeholder-policy\" : \"DENY\"}");
assertEquals(ext.getJSONStructure().asJsonObject(), entity.toJSONObject());
@@ -138,6 +143,7 @@
entity.setExcludes(null);
entity.setIncludes(null);
entity.setDefaultValue(null);
+ entity.setPlaceholderPolicy(null);
ext.setJSON("{ \"variable\" : \"var\", \"regex\": \".\"}");
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/validation/PropertyValidatorTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/validation/PropertyValidatorTest.java
index f62b9e9..2ad6fab 100644
--- a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/validation/PropertyValidatorTest.java
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/validation/PropertyValidatorTest.java
@@ -27,6 +27,7 @@
import org.apache.sling.feature.extension.apiregions.api.config.Mode;
import org.apache.sling.feature.extension.apiregions.api.config.Option;
+import org.apache.sling.feature.extension.apiregions.api.config.PlaceholderPolicy;
import org.apache.sling.feature.extension.apiregions.api.config.PropertyDescription;
import org.apache.sling.feature.extension.apiregions.api.config.PropertyType;
import org.apache.sling.feature.extension.apiregions.api.config.Range;
@@ -477,4 +478,34 @@
assertFalse(result.isValid());
assertTrue(result.isSkipped());
}
-}
+
+ @Test public void testPlaceholderPolicyRequire() {
+ final PropertyDescription desc = new PropertyDescription();
+ desc.setPlaceholderPolicy(PlaceholderPolicy.REQUIRE);
+
+ PropertyValidationResult result = null;
+
+ result = validator.validate("$[env:variable]", desc);
+ assertTrue(result.isValid());
+ assertFalse(result.isSkipped());
+
+ result = validator.validate("hello", desc);
+ assertFalse(result.isValid());
+ assertFalse(result.isSkipped());
+ }
+
+ @Test public void testPlaceholderPolicyDeny() {
+ final PropertyDescription desc = new PropertyDescription();
+ desc.setPlaceholderPolicy(PlaceholderPolicy.DENY);
+
+ PropertyValidationResult result = null;
+
+ result = validator.validate("$[env:variable]", desc);
+ assertFalse(result.isValid());
+ assertFalse(result.isSkipped());
+
+ result = validator.validate("hello", desc);
+ assertTrue(result.isValid());
+ assertFalse(result.isSkipped());
+ }
+}
\ No newline at end of file