From 68f57aa953015d3145efa2bacdeff97e2448ee88 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Wed, 11 Nov 2009 16:39:20 +0000 Subject: [PATCH] SPR-6308 - Spring Expression Language creates systemProperties bean calling System.getProperties() which in enterprise shared containers is locked down --- .../support/AbstractApplicationContext.java | 37 ++++--- .../support/ReadOnlySystemAttributesMap.java | 98 +++++++++++++++++++ .../ApplicationContextExpressionTests.java | 41 ++++++++ 3 files changed, 165 insertions(+), 11 deletions(-) create mode 100644 org.springframework.context/src/main/java/org/springframework/context/support/ReadOnlySystemAttributesMap.java diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java index b0f4e6c8e99..1747b8fa455 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java +++ b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java @@ -20,13 +20,11 @@ import java.io.IOException; import java.lang.annotation.Annotation; import java.security.AccessControlException; import java.util.ArrayList; -import java.util.Collections; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -498,15 +496,24 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader // Register default environment beans. if (!beanFactory.containsBean(SYSTEM_PROPERTIES_BEAN_NAME)) { - Properties systemProperties; + Map systemProperties; try { systemProperties = System.getProperties(); } catch (AccessControlException ex) { - if (logger.isInfoEnabled()) { - logger.info("Not allowed to obtain system properties: " + ex.getMessage()); - } - systemProperties = new Properties(); + systemProperties = new ReadOnlySystemAttributesMap() { + + @Override + protected String getSystemAttribute(String propertyName) { + try { + return System.getProperty(propertyName); + } catch (AccessControlException ex) { + logger.info("Not allowed to obtain system property [" + propertyName + "]: " + + ex.getMessage()); + return null; + } + } + }; } beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, systemProperties); } @@ -516,10 +523,18 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader systemEnvironment = System.getenv(); } catch (AccessControlException ex) { - if (logger.isInfoEnabled()) { - logger.info("Not allowed to obtain system environment: " + ex.getMessage()); - } - systemEnvironment = Collections.emptyMap(); + systemEnvironment = new ReadOnlySystemAttributesMap() { + @Override + protected String getSystemAttribute(String variableName) { + try { + return System.getenv(variableName); + } catch (AccessControlException ex) { + logger.info("Not allowed to obtain system environment variable [" + variableName + "]: " + + ex.getMessage()); + return null; + } + } + }; } beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, systemEnvironment); } diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/ReadOnlySystemAttributesMap.java b/org.springframework.context/src/main/java/org/springframework/context/support/ReadOnlySystemAttributesMap.java new file mode 100644 index 00000000000..992f58404a5 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/context/support/ReadOnlySystemAttributesMap.java @@ -0,0 +1,98 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed 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.springframework.context.support; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * Read-only {@code Map} implementation that is backed by system properties or environment + * variables. + * + *

Used by {@link AbstractApplicationContext} when a {@link SecurityManager} prohibits access to {@link + * System#getProperties()} or {@link System#getenv()}. + * + * @author Arjen Poutsma + * @since 3.0 + */ +abstract class ReadOnlySystemAttributesMap implements Map { + + public boolean containsKey(Object key) { + return get(key) != null; + } + + public String get(Object key) { + if (key instanceof String) { + String attributeName = (String) key; + return getSystemAttribute(attributeName); + } + else { + return null; + } + } + + public boolean isEmpty() { + return false; + } + + /** + * Template method that returns the underlying system attribute. + * + *

Implementations typically call {@link System#getProperty(String)} or {@link System#getenv(String)} here. + */ + protected abstract String getSystemAttribute(String attributeName); + + // Unsupported + + public int size() { + throw new UnsupportedOperationException(); + } + + public String put(String key, String value) { + throw new UnsupportedOperationException(); + } + + public boolean containsValue(Object value) { + throw new UnsupportedOperationException(); + } + + public String remove(Object key) { + throw new UnsupportedOperationException(); + } + + public void clear() { + throw new UnsupportedOperationException(); + } + + public Set keySet() { + throw new UnsupportedOperationException(); + } + + public void putAll(Map m) { + throw new UnsupportedOperationException(); + } + + public Collection values() { + throw new UnsupportedOperationException(); + } + + public Set> entrySet() { + throw new UnsupportedOperationException(); + } + +} diff --git a/org.springframework.context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java b/org.springframework.context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java index 3cf9cede758..8c0de571d34 100644 --- a/org.springframework.context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java +++ b/org.springframework.context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java @@ -17,6 +17,8 @@ package org.springframework.context.expression; import java.io.Serializable; +import java.security.AccessControlException; +import java.security.Permission; import java.util.Properties; import org.apache.commons.logging.Log; @@ -236,6 +238,45 @@ public class ApplicationContextExpressionTests { assertTrue("Prototype creation took too long: " + sw.getTotalTimeMillis(), sw.getTotalTimeMillis() < 6000); } + @Test + public void systemPropertiesSecurityManager() { + GenericApplicationContext ac = new GenericApplicationContext(); + AnnotationConfigUtils.registerAnnotationConfigProcessors(ac); + + + GenericBeanDefinition bd = new GenericBeanDefinition(); + bd.setBeanClass(TestBean.class); + bd.getPropertyValues().addPropertyValue("country", "#{systemProperties.country}"); + ac.registerBeanDefinition("tb", bd); + + SecurityManager oldSecurityManager = System.getSecurityManager(); + try { + System.setProperty("country", "NL"); + + SecurityManager securityManager = new SecurityManager() { + @Override + public void checkPropertiesAccess() { + throw new AccessControlException("Not Allowed"); + } + + @Override + public void checkPermission(Permission perm) { + // allow everything else + } + + }; + System.setSecurityManager(securityManager); + ac.refresh(); + + TestBean tb = ac.getBean("tb", TestBean.class); + assertEquals("NL", tb.getCountry()); + + } + finally { + System.setSecurityManager(oldSecurityManager); + System.getProperties().remove("country"); + } + } public static class ValueTestBean implements Serializable {