FELIX-3168 Support new Password metatype
- Use private constant for the type code to not create an import dependency
- Also use password UI for string properties whose name contains "password"
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1215509 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManager.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManager.java
index 9ac756d..4e31674 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManager.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManager.java
@@ -21,6 +21,7 @@
import java.lang.reflect.Array;
import java.util.*;
import java.util.Map.Entry;
+import java.util.regex.Pattern;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@@ -51,6 +52,27 @@
private static final String PLACEHOLDER_PID = "[Temporary PID replaced by real PID upon save]";
+ /**
+ * Attribute type code for PASSWORD attributes as defined in
+ * Metatype Service Specification 1.2. Since we cannot yet refer
+ * to the 1.2 API package we just replicate the type code here. Once
+ * the API is available and can be referred to, we should use it.
+ */
+ private static final int ATTRIBUTE_TYPE_PASSWORD = 12;
+
+ /**
+ * Marker value of password fields used as dummy values and
+ * indicating unmodified values.
+ */
+ private static final String PASSWORD_PLACEHOLDER_VALUE = "unmodified";
+
+ /**
+ * A regular expression pattern to match against property names to
+ * decide whether the property is hidden or not.
+ */
+ private static final Pattern PASSWORD_PROPERTY = Pattern.compile("password", Pattern.CASE_INSENSITIVE
+ | Pattern.UNICODE_CASE);
+
// templates
private final String TEMPLATE;
@@ -631,16 +653,12 @@
if ( props != null )
{
- json.key( "title" );
- json.value( pid );
- json.key( "description" );
- json.value( "Please enter configuration properties for this configuration in the field below. This configuration has no associated description" );
+ json.key( "title" ).value( pid );
+ json.key( "description" ).value( "Please enter configuration properties for this configuration in the field below. This configuration has no associated description" );
- json.key( "propertylist" );
- json.value( "properties" );
+ json.key( "propertylist" ).value( "properties" );
- json.key( "properties" );
- json.object();
+ json.key( "properties" ).object();
for ( Enumeration pe = props.keys(); pe.hasMoreElements(); )
{
Object key = pe.nextElement();
@@ -652,8 +670,7 @@
&& !key.equals( ConfigurationAdmin.SERVICE_BUNDLELOCATION )
&& !key.equals( ConfigurationAdmin.SERVICE_FACTORYPID ) )
{
- json.key( String.valueOf( key ) );
- json.value( props.get( key ) );
+ json.key( String.valueOf( key ) ).value( props.get( key ) );
}
}
json.endObject();
@@ -694,98 +711,12 @@
JSONArray propertyList = new JSONArray();
- for ( int i = 0; i < ad.length; i++ )
+ for ( AttributeDefinition adi : ad )
{
- json.key( ad[i].getID() );
- json.object();
-
- Object value = props.get( ad[i].getID() );
- if ( value == null )
- {
- value = ad[i].getDefaultValue();
- if ( value == null )
- {
- if ( ad[i].getCardinality() == 0 )
- {
- value = "";
- }
- else
- {
- value = new String[0];
- }
- }
- }
-
- json.key( "name" );
- json.value( ad[i].getName() );
-
- json.key( "type" );
- if ( ad[i].getOptionLabels() != null && ad[i].getOptionLabels().length > 0 )
- {
- json.object();
- json.key( "labels" );
- json.value( Arrays.asList( ad[i].getOptionLabels() ) );
- json.key( "values" );
- json.value( Arrays.asList( ad[i].getOptionValues() ) );
- json.endObject();
- }
- else
- {
- json.value( ad[i].getType() );
- }
-
- if ( ad[i].getCardinality() == 0 )
- {
- // scalar
- if ( value instanceof Vector )
- {
- value = ( ( Vector ) value ).get( 0 );
- }
- else if ( value.getClass().isArray() )
- {
- value = Array.get( value, 0 );
- }
- json.key( "value" );
- json.value( value );
- }
- else
- {
- if ( value instanceof Vector )
- {
- value = new JSONArray( ( Vector ) value );
- }
- else if ( value.getClass().isArray() )
- {
- if ( value.getClass().getComponentType().isPrimitive() )
- {
- final int len = Array.getLength(value);
- final Object[] tmp = new Object[len];
- for ( int j = 0; j < len; j++ )
- {
- tmp[j] = Array.get(value, j);
- }
- value = tmp;
- }
- value = new JSONArray( Arrays.asList( ( Object[] ) value ) );
- }
- else
- {
- JSONArray tmp = new JSONArray();
- tmp.put( value );
- value = tmp;
- }
- json.key( "values" );
- json.value( value );
- }
-
- if ( ad[i].getDescription() != null )
- {
- json.key( "description" );
- json.value( ad[i].getDescription() + " (" + ad[i].getID() + ")" );
- }
-
- json.endObject();
- propertyList.put( ad[i].getID() );
+ final String attrId = adi.getID();
+ json.key( attrId );
+ attributeToJson( json, adi, props.get( attrId ) );
+ propertyList.put( attrId );
}
json.key( "propertylist" );
@@ -920,10 +851,12 @@
{
String propName = propTokens.nextToken();
AttributeDefinition ad = ( AttributeDefinition ) adMap.get( propName );
- if ( ad == null || ( ad.getCardinality() == 0 && ad.getType() == AttributeDefinition.STRING ) )
+ if ( ad == null
+ || ( ad.getCardinality() == 0 && ( ad.getType() == AttributeDefinition.STRING || ad.getType() == ATTRIBUTE_TYPE_PASSWORD ) ) )
{
String prop = request.getParameter( propName );
- if ( prop != null )
+ if ( prop != null
+ && ( ad.getType() != ATTRIBUTE_TYPE_PASSWORD || !PASSWORD_PLACEHOLDER_VALUE.equals( prop ) ) )
{
props.put( propName, prop );
}
@@ -952,15 +885,22 @@
String[] properties = request.getParameterValues( propName );
if ( properties != null )
{
- for ( int i = 0; i < properties.length; i++ )
+ if ( ad.getType() == ATTRIBUTE_TYPE_PASSWORD )
{
- try
+ setPasswordProps( vec, properties, props.get( propName ) );
+ }
+ else
+ {
+ for ( int i = 0; i < properties.length; i++ )
{
- vec.add( toType( ad.getType(), properties[i] ) );
- }
- catch ( NumberFormatException nfe )
- {
- // don't care
+ try
+ {
+ vec.add( toType( ad.getType(), properties[i] ) );
+ }
+ catch ( NumberFormatException nfe )
+ {
+ // don't care
+ }
}
}
}
@@ -1006,6 +946,106 @@
}
+ private static void attributeToJson( final JSONWriter json, final AttributeDefinition ad, final Object propValue )
+ throws JSONException
+ {
+ json.object();
+
+ Object value;
+ if ( propValue != null )
+ {
+ value = propValue;
+ }
+ else if ( ad.getDefaultValue() != null )
+ {
+ value = ad.getDefaultValue();
+ }
+ else if ( ad.getCardinality() == 0 )
+ {
+ value = "";
+ }
+ else
+ {
+ value = new String[0];
+ }
+
+ json.key( "name" );
+ json.value( ad.getName() );
+
+ // attribute type - overwrite metatype provided type
+ // if the property name contains "password" and the
+ // type is string
+ int propertyType = getAttributeType( ad );
+
+ json.key( "type" );
+ if ( ad.getOptionLabels() != null && ad.getOptionLabels().length > 0 )
+ {
+ json.object();
+ json.key( "labels" );
+ json.value( Arrays.asList( ad.getOptionLabels() ) );
+ json.key( "values" );
+ json.value( Arrays.asList( ad.getOptionValues() ) );
+ json.endObject();
+ }
+ else
+ {
+ json.value( propertyType );
+ }
+
+ // unless the property is of password type, send it
+ final boolean isPassword = propertyType == ATTRIBUTE_TYPE_PASSWORD;
+ if ( ad.getCardinality() == 0 )
+ {
+ // scalar
+ if ( isPassword )
+ {
+ value = PASSWORD_PLACEHOLDER_VALUE;
+ }
+ else if ( value instanceof Vector )
+ {
+ value = ( ( Vector ) value ).get( 0 );
+ }
+ else if ( value.getClass().isArray() )
+ {
+ value = Array.get( value, 0 );
+ }
+ json.key( "value" );
+ json.value( value );
+ }
+ else
+ {
+ value = new JSONArray( toList( value ) );
+ if ( isPassword )
+ {
+ JSONArray tmp = ( JSONArray ) value;
+ for ( int tmpI = 0; tmpI < tmp.length(); tmpI++ )
+ {
+ tmp.put( tmpI, PASSWORD_PLACEHOLDER_VALUE );
+ }
+ }
+ json.key( "values" );
+ json.value( value );
+ }
+
+ if ( ad.getDescription() != null )
+ {
+ json.key( "description" );
+ json.value( ad.getDescription() + " (" + ad.getID() + ")" );
+ }
+
+ json.endObject();
+ }
+
+ private static int getAttributeType( final AttributeDefinition ad )
+ {
+ if ( ad.getType() == AttributeDefinition.STRING && PASSWORD_PROPERTY.matcher( ad.getID() ).find() )
+ {
+ return ATTRIBUTE_TYPE_PASSWORD;
+ }
+ return ad.getType();
+ }
+
+
/**
* @throws NumberFormatException If the value cannot be converted to
* a number and type indicates a numeric type
@@ -1031,20 +1071,67 @@
return Integer.valueOf( value );
case AttributeDefinition.SHORT:
return Short.valueOf( value );
-
default:
// includes AttributeDefinition.STRING
+ // includes ATTRIBUTE_TYPE_PASSWORD/AttributeDefinition.PASSWORD
return value;
}
}
+ private static List toList( Object value )
+ {
+ if ( value instanceof Vector )
+ {
+ return ( Vector ) value;
+ }
+ else if ( value.getClass().isArray() )
+ {
+ if ( value.getClass().getComponentType().isPrimitive() )
+ {
+ final int len = Array.getLength( value );
+ final Object[] tmp = new Object[len];
+ for ( int j = 0; j < len; j++ )
+ {
+ tmp[j] = Array.get( value, j );
+ }
+ value = tmp;
+ }
+ return Arrays.asList( ( Object[] ) value );
+ }
+ else
+ {
+ return Collections.singletonList( value );
+ }
+ }
+
+
+ private static void setPasswordProps( final Vector vec, final String[] properties, Object props )
+ {
+ List propList = toList( props );
+ for ( int i = 0; i < properties.length; i++ )
+ {
+ if ( PASSWORD_PLACEHOLDER_VALUE.equals( properties[i] ) )
+ {
+ if ( i < propList.size() && propList.get( i ) != null )
+ {
+ vec.add( propList.get( i ) );
+ }
+ }
+ else
+ {
+ vec.add( properties[i] );
+ }
+ }
+ }
+
+
private static final Object toArray( int type, Vector values )
{
int size = values.size();
// short cut for string array
- if ( type == AttributeDefinition.STRING )
+ if ( type == AttributeDefinition.STRING || type == ATTRIBUTE_TYPE_PASSWORD )
{
return values.toArray( new String[size] );
}
@@ -1152,4 +1239,5 @@
}
+
}
diff --git a/webconsole/src/main/resources/res/ui/config.js b/webconsole/src/main/resources/res/ui/config.js
index 8b93e7e..eead16b 100644
--- a/webconsole/src/main/resources/res/ui/config.js
+++ b/webconsole/src/main/resources/res/ui/config.js
@@ -348,8 +348,10 @@
return selectEl;
} else { // Simple
+ // Metatype 1.2: Attr type 12 is PASSWORD
+ var elType = (type == 12) ? "password" : "text";
return createElement( "input", null, {
- type: "text",
+ type: elType,
name: prop,
value: value,
style: { width: width }