This is an automated email from the ASF dual-hosted git repository.
ddekany pushed a commit to branch 2.3-gae
in repository https://gitbox.apache.org/repos/asf/freemarker.git
The following commit(s) were added to refs/heads/2.3-gae by this push:
new 66a2704 Fixed issue where StaticModel didn't consider the
MemberAccessPolicy when exposing fields (as that wasn't filtered at all before
2.3.30). Also simplified related ClassIntrospector API a bit.
66a2704 is described below
commit 66a2704c5c04c50212edac6c68f94986829572e7
Author: ddekany <[email protected]>
AuthorDate: Tue Jan 14 07:33:57 2020 +0100
Fixed issue where StaticModel didn't consider the MemberAccessPolicy when
exposing fields (as that wasn't filtered at all before 2.3.30). Also simplified
related ClassIntrospector API a bit.
---
.../ext/beans/AllowAllMemberAccessPolicy.java | 53 +++++++
.../freemarker/ext/beans/ClassIntrospector.java | 57 +++-----
.../java/freemarker/ext/beans/StaticModel.java | 11 +-
...DefaultObjectWrapperMemberAccessPolicyTest.java | 160 +++++++++++++++++++++
4 files changed, 242 insertions(+), 39 deletions(-)
diff --git a/src/main/java/freemarker/ext/beans/AllowAllMemberAccessPolicy.java
b/src/main/java/freemarker/ext/beans/AllowAllMemberAccessPolicy.java
new file mode 100644
index 0000000..dd9a1fc
--- /dev/null
+++ b/src/main/java/freemarker/ext/beans/AllowAllMemberAccessPolicy.java
@@ -0,0 +1,53 @@
+/*
+ * 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 freemarker.ext.beans;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+final class AllowAllMemberAccessPolicy implements MemberAccessPolicy {
+ public static final AllowAllMemberAccessPolicy INSTANCE = new
AllowAllMemberAccessPolicy();
+
+ private AllowAllMemberAccessPolicy() {
+ }
+
+ public static final ClassMemberAccessPolicy CLASS_POLICY_INSTANCE = new
ClassMemberAccessPolicy() {
+ @Override
+ public boolean isMethodExposed(Method method) {
+ return true;
+ }
+
+ @Override
+ public boolean isConstructorExposed(Constructor<?> constructor) {
+ return true;
+ }
+
+ @Override
+ public boolean isFieldExposed(Field field) {
+ return true;
+ }
+ };
+
+ @Override
+ public ClassMemberAccessPolicy forClass(Class<?> contextClass) {
+ return CLASS_POLICY_INSTANCE;
+ }
+}
diff --git a/src/main/java/freemarker/ext/beans/ClassIntrospector.java
b/src/main/java/freemarker/ext/beans/ClassIntrospector.java
index 630bf95..aa3bafb 100644
--- a/src/main/java/freemarker/ext/beans/ClassIntrospector.java
+++ b/src/main/java/freemarker/ext/beans/ClassIntrospector.java
@@ -272,26 +272,26 @@ class ClassIntrospector {
*/
private Map<Object, Object> createClassIntrospectionData(Class<?> clazz) {
final Map<Object, Object> introspData = new HashMap<Object, Object>();
- ClassMemberAccessPolicy classMemberAccessPolicy =
getClassMemberAccessPolicyIfNotIgnored(clazz);
+ ClassMemberAccessPolicy effClassMemberAccessPolicy =
getEffectiveClassMemberAccessPolicy(clazz);
if (exposeFields) {
- addFieldsToClassIntrospectionData(introspData, clazz,
classMemberAccessPolicy);
+ addFieldsToClassIntrospectionData(introspData, clazz,
effClassMemberAccessPolicy);
}
final Map<ExecutableMemberSignature, List<Method>> accessibleMethods =
discoverAccessibleMethods(clazz);
- addGenericGetToClassIntrospectionData(introspData, accessibleMethods,
classMemberAccessPolicy);
+ addGenericGetToClassIntrospectionData(introspData, accessibleMethods,
effClassMemberAccessPolicy);
if (exposureLevel != BeansWrapper.EXPOSE_NOTHING) {
try {
- addBeanInfoToClassIntrospectionData(introspData, clazz,
accessibleMethods, classMemberAccessPolicy);
+ addBeanInfoToClassIntrospectionData(introspData, clazz,
accessibleMethods, effClassMemberAccessPolicy);
} catch (IntrospectionException e) {
LOG.warn("Couldn't properly perform introspection for class "
+ clazz, e);
introspData.clear(); // FIXME NBC: Don't drop everything here.
}
}
- addConstructorsToClassIntrospectionData(introspData, clazz,
classMemberAccessPolicy);
+ addConstructorsToClassIntrospectionData(introspData, clazz,
effClassMemberAccessPolicy);
if (introspData.size() > 1) {
return introspData;
@@ -304,12 +304,12 @@ class ClassIntrospector {
}
private void addFieldsToClassIntrospectionData(Map<Object, Object>
introspData, Class<?> clazz,
- ClassMemberAccessPolicy classMemberAccessPolicy) throws
SecurityException {
+ ClassMemberAccessPolicy effClassMemberAccessPolicy) throws
SecurityException {
Field[] fields = clazz.getFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
if ((field.getModifiers() & Modifier.STATIC) == 0) {
- if (classMemberAccessPolicy == null ||
classMemberAccessPolicy.isFieldExposed(field)) {
+ if (effClassMemberAccessPolicy.isFieldExposed(field)) {
introspData.put(field.getName(), field);
}
}
@@ -319,7 +319,7 @@ class ClassIntrospector {
private void addBeanInfoToClassIntrospectionData(
Map<Object, Object> introspData, Class<?> clazz,
Map<ExecutableMemberSignature, List<Method>> accessibleMethods,
- ClassMemberAccessPolicy classMemberAccessPolicy) throws
IntrospectionException {
+ ClassMemberAccessPolicy effClassMemberAccessPolicy) throws
IntrospectionException {
BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
List<PropertyDescriptor> pdas = getPropertyDescriptors(beanInfo,
clazz);
int pdasLength = pdas.size();
@@ -327,7 +327,7 @@ class ClassIntrospector {
for (int i = pdasLength - 1; i >= 0; --i) {
addPropertyDescriptorToClassIntrospectionData(
introspData, pdas.get(i),
- accessibleMethods, classMemberAccessPolicy);
+ accessibleMethods, effClassMemberAccessPolicy);
}
if (exposureLevel < BeansWrapper.EXPOSE_PROPERTIES_ONLY) {
@@ -339,7 +339,7 @@ class ClassIntrospector {
IdentityHashMap<Method, Void> argTypesUsedByIndexerPropReaders =
null;
for (int i = mdsSize - 1; i >= 0; --i) {
final Method method =
getMatchingAccessibleMethod(mds.get(i).getMethod(), accessibleMethods);
- if (method != null &&
(isMethodExposed(classMemberAccessPolicy, method))) {
+ if (method != null &&
effClassMemberAccessPolicy.isMethodExposed(method)) {
decision.setDefaults(method);
if (methodAppearanceFineTuner != null) {
if (decisionInput == null) {
@@ -356,7 +356,7 @@ class ClassIntrospector {
(decision.getReplaceExistingProperty()
|| !(introspData.get(propDesc.getName())
instanceof FastPropertyDescriptor))) {
addPropertyDescriptorToClassIntrospectionData(
- introspData, propDesc, accessibleMethods,
classMemberAccessPolicy);
+ introspData, propDesc, accessibleMethods,
effClassMemberAccessPolicy);
}
String methodKey = decision.getExposeMethodAs();
@@ -667,9 +667,9 @@ class ClassIntrospector {
private void addPropertyDescriptorToClassIntrospectionData(Map<Object,
Object> introspData,
PropertyDescriptor pd,
Map<ExecutableMemberSignature, List<Method>> accessibleMethods,
- ClassMemberAccessPolicy classMemberAccessPolicy) {
+ ClassMemberAccessPolicy effClassMemberAccessPolicy) {
Method readMethod = getMatchingAccessibleMethod(pd.getReadMethod(),
accessibleMethods);
- if (readMethod != null && !isMethodExposed(classMemberAccessPolicy,
readMethod)) {
+ if (readMethod != null &&
!effClassMemberAccessPolicy.isMethodExposed(readMethod)) {
readMethod = null;
}
@@ -677,7 +677,7 @@ class ClassIntrospector {
if (pd instanceof IndexedPropertyDescriptor) {
indexedReadMethod = getMatchingAccessibleMethod(
((IndexedPropertyDescriptor) pd).getIndexedReadMethod(),
accessibleMethods);
- if (indexedReadMethod != null &&
!isMethodExposed(classMemberAccessPolicy, indexedReadMethod)) {
+ if (indexedReadMethod != null &&
!effClassMemberAccessPolicy.isMethodExposed(indexedReadMethod)) {
indexedReadMethod = null;
}
if (indexedReadMethod != null) {
@@ -695,23 +695,23 @@ class ClassIntrospector {
private void addGenericGetToClassIntrospectionData(Map<Object, Object>
introspData,
Map<ExecutableMemberSignature, List<Method>> accessibleMethods,
- ClassMemberAccessPolicy classMemberAccessPolicy) {
+ ClassMemberAccessPolicy effClassMemberAccessPolicy) {
Method genericGet = getFirstAccessibleMethod(GET_STRING_SIGNATURE,
accessibleMethods);
if (genericGet == null) {
genericGet = getFirstAccessibleMethod(GET_OBJECT_SIGNATURE,
accessibleMethods);
}
- if (genericGet != null && isMethodExposed(classMemberAccessPolicy,
genericGet)) {
+ if (genericGet != null &&
effClassMemberAccessPolicy.isMethodExposed(genericGet)) {
introspData.put(GENERIC_GET_KEY, genericGet);
}
}
private void addConstructorsToClassIntrospectionData(final Map<Object,
Object> introspData,
- Class<?> clazz, ClassMemberAccessPolicy classMemberAccessPolicy) {
+ Class<?> clazz, ClassMemberAccessPolicy
effClassMemberAccessPolicy) {
try {
Constructor<?>[] ctorsUnfiltered = clazz.getConstructors();
List<Constructor<?>> ctors = new
ArrayList<Constructor<?>>(ctorsUnfiltered.length);
for (Constructor<?> ctor : ctorsUnfiltered) {
- if (classMemberAccessPolicy == null ||
classMemberAccessPolicy.isConstructorExposed(ctor)) {
+ if (effClassMemberAccessPolicy.isConstructorExposed(ctor)) {
ctors.add(ctor);
}
}
@@ -828,23 +828,12 @@ class ClassIntrospector {
}
/**
- * Returns the {@link ClassMemberAccessPolicy}, or {@code null} if it
should be ignored because of other settings.
- * (Ideally, all such rules should be contained in {@link
ClassMemberAccessPolicy} alone, but that interface was
- * added late in history.)
- *
- * @see #isMethodExposed(ClassMemberAccessPolicy, Method)
+ * Returns the {@link ClassMemberAccessPolicy} to actually use, which is
not just
+ * {@link BeansWrapper#getMemberAccessPolicy()} if {@link
BeansWrapper#getExposureLevel()} is more allowing than
+ * {@link BeansWrapper#EXPOSE_SAFE}. {@link BeansWrapper#EXPOSE_NOTHING}
though is not factored in here.
*/
- ClassMemberAccessPolicy getClassMemberAccessPolicyIfNotIgnored(Class
containingClass) {
- return exposureLevel < BeansWrapper.EXPOSE_SAFE ? null :
memberAccessPolicy.forClass(containingClass);
- }
-
- /**
- * @param classMemberAccessPolicyIfNotIgnored
- * The value returned by {@link
#getClassMemberAccessPolicyIfNotIgnored(Class)}
- */
- static boolean isMethodExposed(ClassMemberAccessPolicy
classMemberAccessPolicyIfNotIgnored, Method method) {
- return classMemberAccessPolicyIfNotIgnored == null
- || classMemberAccessPolicyIfNotIgnored.isMethodExposed(method);
+ ClassMemberAccessPolicy getEffectiveClassMemberAccessPolicy(Class<?>
containingClass) {
+ return exposureLevel < BeansWrapper.EXPOSE_SAFE ?
AllowAllMemberAccessPolicy.CLASS_POLICY_INSTANCE :
memberAccessPolicy.forClass(containingClass);
}
private boolean is2321Bugfixed() {
diff --git a/src/main/java/freemarker/ext/beans/StaticModel.java
b/src/main/java/freemarker/ext/beans/StaticModel.java
index fc7504d..cd1bf97 100644
--- a/src/main/java/freemarker/ext/beans/StaticModel.java
+++ b/src/main/java/freemarker/ext/beans/StaticModel.java
@@ -106,10 +106,13 @@ final class StaticModel implements TemplateHashModelEx {
return;
}
+ ClassMemberAccessPolicy effClassMemberAccessPolicy =
+
wrapper.getClassIntrospector().getEffectiveClassMemberAccessPolicy(clazz);
+
Field[] fields = clazz.getFields();
for (Field field : fields) {
int mod = field.getModifiers();
- if (Modifier.isPublic(mod) && Modifier.isStatic(mod)) {
+ if (Modifier.isPublic(mod) && Modifier.isStatic(mod) &&
effClassMemberAccessPolicy.isFieldExposed(field)) {
if (Modifier.isFinal(mod)) {
try {
// public static final fields are evaluated once and
@@ -127,14 +130,12 @@ final class StaticModel implements TemplateHashModelEx {
}
}
if (wrapper.getExposureLevel() < BeansWrapper.EXPOSE_PROPERTIES_ONLY) {
- ClassMemberAccessPolicy classMemberAccessPolicy =
-
wrapper.getClassIntrospector().getClassMemberAccessPolicyIfNotIgnored(clazz);
Method[] methods = clazz.getMethods();
for (int i = 0; i < methods.length; ++i) {
Method method = methods[i];
int mod = method.getModifiers();
if (Modifier.isPublic(mod) && Modifier.isStatic(mod)
- &&
ClassIntrospector.isMethodExposed(classMemberAccessPolicy, method)) {
+ && effClassMemberAccessPolicy.isMethodExposed(method))
{
String name = method.getName();
Object obj = map.get(name);
if (obj instanceof Method) {
@@ -149,7 +150,7 @@ final class StaticModel implements TemplateHashModelEx {
if (obj != null) {
if (LOG.isInfoEnabled()) {
LOG.info("Overwriting value [" + obj + "] for
" +
- " key '" + name + "' with [" + method
+
+ " key '" + name + "' with [" + method +
"] in static model for " +
clazz.getName());
}
}
diff --git
a/src/test/java/freemarker/ext/beans/DefaultObjectWrapperMemberAccessPolicyTest.java
b/src/test/java/freemarker/ext/beans/DefaultObjectWrapperMemberAccessPolicyTest.java
index f9fcf47..0938541 100644
---
a/src/test/java/freemarker/ext/beans/DefaultObjectWrapperMemberAccessPolicyTest.java
+++
b/src/test/java/freemarker/ext/beans/DefaultObjectWrapperMemberAccessPolicyTest.java
@@ -152,6 +152,56 @@ public class DefaultObjectWrapperMemberAccessPolicyTest {
}
@Test
+ public void testExposeAllWithCustomMemberAccessPolicy() throws
TemplateModelException {
+ {
+ DefaultObjectWrapperBuilder owb = new
DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_30);
+ owb.setExposureLevel(DefaultObjectWrapper.EXPOSE_ALL);
+ owb.setExposeFields(true);
+ owb.setMemberAccessPolicy(AllowNothingMemberAccessPolicy.INSTANCE);
+ DefaultObjectWrapper ow = owb.build();
+
+ TemplateHashModel objM = (TemplateHashModel) ow.wrap(new C());
+ // Because the MemberAccessPolicy is ignored:
+ assertNotNull(objM.get("publicField1"));
+ assertNotNull(objM.get("m1"));
+ assertNotNull(objM.get("M1"));
+ assertNotNull(objM.get("notify"));
+ assertNull(objM.get("STATIC_FIELD")); // Because it's static
+
+ TemplateHashModel staticModel = (TemplateHashModel)
ow.getStaticModels().get(C.class.getName());
+ assertNotNull(staticModel.get("M1"));
+ assertNotNull(staticModel.get("STATIC_FIELD"));
+ }
+ {
+ DefaultObjectWrapperBuilder owb = new
DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_30);
+ owb.setExposeFields(true);
+ owb.setMemberAccessPolicy(AllowNothingMemberAccessPolicy.INSTANCE);
+ DefaultObjectWrapper ow = owb.build();
+
+ TemplateHashModel objM = (TemplateHashModel) ow.wrap(new C());
+ // Because the MemberAccessPolicy is ignored:
+ assertNull(objM.get("publicField1"));
+ assertNull(objM.get("m1"));
+ assertNull(objM.get("M1"));
+ assertNull(objM.get("notify"));
+ assertNull(objM.get("STATIC_FIELD"));
+
+ TemplateHashModel staticModel = (TemplateHashModel)
ow.getStaticModels().get(C.class.getName());
+ try {
+ assertNull(staticModel.get("M1"));
+ } catch (TemplateModelException e) {
+ assertThat(e.getMessage(), containsString("No such key"));
+ }
+ try {
+ assertNull(staticModel.get("STATIC_FIELD"));
+ fail();
+ } catch (TemplateModelException e) {
+ assertThat(e.getMessage(), containsString("No such key"));
+ }
+ }
+ }
+
+ @Test
public void testExposeFieldsWithDefaultMemberAccessPolicy() throws
TemplateModelException {
DefaultObjectWrapperBuilder owb = new
DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_30);
owb.setExposeFields(true);
@@ -408,6 +458,78 @@ public class DefaultObjectWrapperMemberAccessPolicyTest {
}
}
+ @Test
+ public void testMemberAccessPolicyAndStatics() throws TemplateException {
+ DefaultObjectWrapperBuilder owb = new
DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_30);
+ owb.setMemberAccessPolicy(new MemberAccessPolicy() {
+ public ClassMemberAccessPolicy forClass(Class<?> contextClass) {
+ return new ClassMemberAccessPolicy() {
+ public boolean isMethodExposed(Method method) {
+ return method.getName().equals("m1");
+ }
+
+ public boolean isConstructorExposed(Constructor<?>
constructor) {
+ return false;
+ }
+
+ public boolean isFieldExposed(Field field) {
+ String name = field.getName();
+ return name.equals("F1") || name.equals("V1");
+ }
+ };
+ }
+ });
+ DefaultObjectWrapper ow = owb.build();
+ TemplateHashModel statics = (TemplateHashModel)
ow.getStaticModels().get(Statics.class.getName());
+
+ assertEquals(1, ((TemplateNumberModel)
statics.get("F1")).getAsNumber());
+ try {
+ statics.get("F2");
+ fail();
+ } catch (TemplateModelException e) {
+ assertThat(e.getMessage(), containsString("No such key"));
+ }
+
+ assertEquals(1, ((TemplateNumberModel)
statics.get("V1")).getAsNumber());
+ try {
+ statics.get("V2");
+ fail();
+ } catch (TemplateModelException e) {
+ assertThat(e.getMessage(), containsString("No such key"));
+ }
+
+ assertEquals(1,
+ ((TemplateNumberModel) ((TemplateMethodModelEx)
statics.get("m1"))
+ .exec(Collections.emptyList()))
+ .getAsNumber());
+ try {
+ assertNull(statics.get("m2"));
+ fail();
+ } catch (TemplateModelException e) {
+ assertThat(e.getMessage(), containsString("No such key"));
+ }
+ }
+
+ @Test
+ public void testMemberAccessPolicyAndStatics2() throws TemplateException {
+ DefaultObjectWrapper defaultOw = new
DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_30).build();
+
+ DefaultObjectWrapperBuilder owb = new
DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_30);
+ owb.setMemberAccessPolicy(LegacyDefaultMemberAccessPolicy.INSTANCE);
+ DefaultObjectWrapper legacyDefaultOw = owb.build();
+
+ for (BeansWrapper ow : new BeansWrapper[] { defaultOw, legacyDefaultOw
}) {
+ TemplateHashModel sys = (TemplateHashModel)
ow.getStaticModels().get(System.class.getName());
+ assertNotNull(sys.get("currentTimeMillis"));
+ try {
+ sys.get("exit");
+ fail();
+ } catch (TemplateModelException e) {
+ assertThat(e.getMessage(), containsString("No such key"));
+ }
+ }
+ }
+
private static Object getHashValue(ObjectWrapperAndUnwrapper ow,
TemplateHashModel objM, String key)
throws TemplateModelException {
return ow.unwrap(objM.get(key));
@@ -475,6 +597,8 @@ public class DefaultObjectWrapperMemberAccessPolicyTest {
public String wait(int otherOverload) {
return "safe wait(" + otherOverload + ")";
}
+
+ public static void M1() { }
}
public static class CExtended extends C {
@@ -519,4 +643,40 @@ public class DefaultObjectWrapperMemberAccessPolicyTest {
}
}
+ public static class Statics {
+ public static final int F1 = 1;
+ public static final int F2 = 2;
+ public static int V1 = 1;
+ public static int V2 = 2;
+ public static int m1() {
+ return 1;
+ }
+ public static int m2() {
+ return 2;
+ }
+ }
+
+ public static class AllowNothingMemberAccessPolicy implements
MemberAccessPolicy {
+ private static final AllowNothingMemberAccessPolicy INSTANCE = new
AllowNothingMemberAccessPolicy();
+
+ @Override
+ public ClassMemberAccessPolicy forClass(Class<?> contextClass) {
+ return new ClassMemberAccessPolicy() {
+ @Override
+ public boolean isMethodExposed(Method method) {
+ return false;
+ }
+
+ @Override
+ public boolean isConstructorExposed(Constructor<?>
constructor) {
+ return false;
+ }
+
+ @Override
+ public boolean isFieldExposed(Field field) {
+ return false;
+ }
+ };
+ }
+ }
}