From 90c4e85a8f8135f069f3f05e4d54a77704769f91 Mon Sep 17 00:00:00 2001 From: Raymond Auge Date: Fri, 8 Jun 2012 11:10:10 -0400 Subject: [PATCH] LPS-27741 Add security by plugin context execution to template engines - We have to initialize/destroy a new template context specific to each plugin (keyed by ClassLoader of the plugin) - Add/Replaced helper utilities to support classloading control (and access to classes/packages) within templates --- .../portal/deploy/hot/HotDeployImpl.java | 7 +- .../portal/freemarker/FreeMarkerManager.java | 59 ++- .../FreeMarkerTemplateContextHelper.java | 4 +- .../freemarker/LiferayObjectConstructor.java | 60 +++ .../LiferayTemplateClassResolver.java | 74 ++++ .../freemarker/PACLFreeMarkerTemplate.java | 65 +++ .../template/TemplateContextHelper.java | 398 ++++++++++++++---- .../com/liferay/portal/util/PropsValues.java | 8 + .../LiferayMethodExceptionEventHandler.java | 41 ++ .../portal/velocity/PACLVelocityTemplate.java | 62 +++ .../portal/velocity/VelocityManager.java | 122 +++++- .../VelocityTemplateContextHelper.java | 14 +- portal-impl/src/portal.properties | 30 ++ .../kernel/template/TemplateManager.java | 2 + .../kernel/template/TemplateManagerUtil.java | 8 + .../liferay/portal/kernel/util/PropsKeys.java | 8 + 16 files changed, 845 insertions(+), 117 deletions(-) create mode 100644 portal-impl/src/com/liferay/portal/freemarker/LiferayObjectConstructor.java create mode 100644 portal-impl/src/com/liferay/portal/freemarker/LiferayTemplateClassResolver.java create mode 100644 portal-impl/src/com/liferay/portal/freemarker/PACLFreeMarkerTemplate.java create mode 100644 portal-impl/src/com/liferay/portal/velocity/LiferayMethodExceptionEventHandler.java create mode 100644 portal-impl/src/com/liferay/portal/velocity/PACLVelocityTemplate.java diff --git a/portal-impl/src/com/liferay/portal/deploy/hot/HotDeployImpl.java b/portal-impl/src/com/liferay/portal/deploy/hot/HotDeployImpl.java index 5109427c8898e3..c26f8ef3619c56 100644 --- a/portal-impl/src/com/liferay/portal/deploy/hot/HotDeployImpl.java +++ b/portal-impl/src/com/liferay/portal/deploy/hot/HotDeployImpl.java @@ -21,6 +21,7 @@ import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.servlet.ServletContextPool; +import com.liferay.portal.kernel.template.TemplateManagerUtil; import com.liferay.portal.kernel.util.BasePortalLifecycle; import com.liferay.portal.kernel.util.HttpUtil; import com.liferay.portal.kernel.util.PortalLifecycle; @@ -105,7 +106,11 @@ public void fireUndeployEvent(HotDeployEvent hotDeployEvent) { _deployedServletContextNames.remove( hotDeployEvent.getServletContextName()); - PACLPolicyManager.unregister(hotDeployEvent.getContextClassLoader()); + ClassLoader classLoader = hotDeployEvent.getContextClassLoader(); + + TemplateManagerUtil.destroy(classLoader); + + PACLPolicyManager.unregister(classLoader); } public void registerListener(HotDeployListener hotDeployListener) { diff --git a/portal-impl/src/com/liferay/portal/freemarker/FreeMarkerManager.java b/portal-impl/src/com/liferay/portal/freemarker/FreeMarkerManager.java index 8d02f686984df1..8d0a774f2e8a5d 100644 --- a/portal-impl/src/com/liferay/portal/freemarker/FreeMarkerManager.java +++ b/portal-impl/src/com/liferay/portal/freemarker/FreeMarkerManager.java @@ -22,6 +22,10 @@ import com.liferay.portal.kernel.template.TemplateException; import com.liferay.portal.kernel.template.TemplateManager; import com.liferay.portal.kernel.util.StringPool; +import com.liferay.portal.security.lang.PortalSecurityManagerThreadLocal; +import com.liferay.portal.security.pacl.PACLClassLoaderUtil; +import com.liferay.portal.security.pacl.PACLPolicy; +import com.liferay.portal.security.pacl.PACLPolicyManager; import com.liferay.portal.template.RestrictedTemplate; import com.liferay.portal.template.TemplateContextHelper; import com.liferay.portal.util.PropsValues; @@ -35,6 +39,7 @@ import java.io.IOException; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * @author Mika Koivisto @@ -63,6 +68,10 @@ public void destroy() { return; } + _classLoaderHelperUtilities.clear(); + + _classLoaderHelperUtilities = null; + _configuration.clearEncodingMap(); _configuration.clearSharedVariables(); _configuration.clearTemplateCache(); @@ -84,11 +93,55 @@ public void destroy() { _templateContextHelper = null; } + public void destroy(ClassLoader classLoader) { + _classLoaderHelperUtilities.remove(classLoader); + } + public Template getTemplate( String templateId, String templateContent, String errorTemplateId, String errorTemplateContent, TemplateContextType templateContextType) { - if (templateContextType.equals(TemplateContextType.EMPTY)) { + if (templateContextType.equals(TemplateContextType.CLASS_LOADER)) { + + // This template will have all of its utilities initialized within + // the class loader of the current thread + + ClassLoader contextClassLoader = + PACLClassLoaderUtil.getContextClassLoader(); + + PACLPolicy threadLocalPACLPolicy = + PortalSecurityManagerThreadLocal.getPACLPolicy(); + + PACLPolicy contextClassLoaderPACLPolicy = + PACLPolicyManager.getPACLPolicy(contextClassLoader); + + try { + PortalSecurityManagerThreadLocal.setPACLPolicy( + contextClassLoaderPACLPolicy); + + Map helperUtilities = + _classLoaderHelperUtilities.get(contextClassLoader); + + if (helperUtilities == null) { + helperUtilities = + _templateContextHelper.getHelperUtilities(); + + _classLoaderHelperUtilities.put( + contextClassLoader, helperUtilities); + } + + return new PACLFreeMarkerTemplate( + templateId, templateContent, errorTemplateId, + errorTemplateContent, helperUtilities, _configuration, + _templateContextHelper, _stringTemplateLoader, + contextClassLoaderPACLPolicy); + } + finally { + PortalSecurityManagerThreadLocal.setPACLPolicy( + threadLocalPACLPolicy); + } + } + else if (templateContextType.equals(TemplateContextType.EMPTY)) { return new FreeMarkerTemplate( templateId, templateContent, errorTemplateId, errorTemplateContent, null, _configuration, @@ -186,6 +239,8 @@ public void init() throws TemplateException { _configuration.setDefaultEncoding(StringPool.UTF8); _configuration.setLocalizedLookup( PropsValues.FREEMARKER_ENGINE_LOCALIZED_LOOKUP); + _configuration.setNewBuiltinClassResolver( + new LiferayTemplateClassResolver()); _configuration.setObjectWrapper(new LiferayObjectWrapper()); _configuration.setTemplateLoader(multiTemplateLoader); _configuration.setTemplateUpdateDelay( @@ -217,6 +272,8 @@ public void setTemplateContextHelper( private static Log _log = LogFactoryUtil.getLog(FreeMarkerManager.class); + private Map> _classLoaderHelperUtilities = + new ConcurrentHashMap>(); private Configuration _configuration; private Map _restrictedHelperUtilities; private Map _standardHelperUtilities; diff --git a/portal-impl/src/com/liferay/portal/freemarker/FreeMarkerTemplateContextHelper.java b/portal-impl/src/com/liferay/portal/freemarker/FreeMarkerTemplateContextHelper.java index a5ddec44e6d6b3..8d762832263d1b 100644 --- a/portal-impl/src/com/liferay/portal/freemarker/FreeMarkerTemplateContextHelper.java +++ b/portal-impl/src/com/liferay/portal/freemarker/FreeMarkerTemplateContextHelper.java @@ -28,8 +28,6 @@ import freemarker.ext.beans.BeansWrapper; -import freemarker.template.utility.ObjectConstructor; - import java.util.Map; import java.util.Set; @@ -52,7 +50,7 @@ public Map getHelperUtilities() { // Object util - helperUtilities.put("objectUtil", new ObjectConstructor()); + helperUtilities.put("objectUtil", new LiferayObjectConstructor()); // Portlet preferences diff --git a/portal-impl/src/com/liferay/portal/freemarker/LiferayObjectConstructor.java b/portal-impl/src/com/liferay/portal/freemarker/LiferayObjectConstructor.java new file mode 100644 index 00000000000000..c1e2a6049b7d0a --- /dev/null +++ b/portal-impl/src/com/liferay/portal/freemarker/LiferayObjectConstructor.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + */ + +package com.liferay.portal.freemarker; + +import com.liferay.portal.security.pacl.PACLClassLoaderUtil; + +import freemarker.ext.beans.BeansWrapper; + +import freemarker.template.TemplateMethodModelEx; +import freemarker.template.TemplateModelException; + +import java.util.List; + +/** + * @author Raymond Augé + */ +public class LiferayObjectConstructor implements TemplateMethodModelEx { + + public Object exec(@SuppressWarnings("rawtypes") List arguments) + throws TemplateModelException { + + if (arguments.isEmpty()) { + throw new TemplateModelException( + "This method must have at least one argument as the name of " + + "the class to instantiate"); + } + + Class clazz = null; + + try { + String className = String.valueOf(arguments.get(0)); + + clazz = Class.forName( + className, true, PACLClassLoaderUtil.getContextClassLoader()); + } + catch (Exception e) { + throw new TemplateModelException(e.getMessage()); + } + + BeansWrapper beansWrapper = BeansWrapper.getDefaultInstance(); + + Object object = beansWrapper.newInstance( + clazz, arguments.subList(1, arguments.size())); + + return beansWrapper.wrap(object); + } + +} \ No newline at end of file diff --git a/portal-impl/src/com/liferay/portal/freemarker/LiferayTemplateClassResolver.java b/portal-impl/src/com/liferay/portal/freemarker/LiferayTemplateClassResolver.java new file mode 100644 index 00000000000000..6cecc341e0c55e --- /dev/null +++ b/portal-impl/src/com/liferay/portal/freemarker/LiferayTemplateClassResolver.java @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + */ + +package com.liferay.portal.freemarker; + +import com.liferay.portal.security.pacl.PACLClassLoaderUtil; +import com.liferay.portal.util.PropsValues; + +import freemarker.core.Environment; +import freemarker.core.TemplateClassResolver; + +import freemarker.template.Template; +import freemarker.template.TemplateException; +import freemarker.template.utility.ObjectConstructor; + +/** + * @author Raymond Augé + */ +public class LiferayTemplateClassResolver implements TemplateClassResolver { + + public Class resolve( + String className, Environment environment, Template template) + throws TemplateException { + + if (className.equals(ObjectConstructor.class.getName())) { + throw new TemplateException( + "Instantiating " + className + " is not allowed in the " + + "template for security reasons", + environment); + } + + for (String restrictedClassName : + PropsValues.FREEMARKER_ENGINE_RESTRICTED_CLASSES) { + + if (className.equals(restrictedClassName)) { + throw new TemplateException( + "Instantiating " + className + " is not allowed in the " + + "template for security reasons", + environment); + } + } + + for (String restrictedPackageName : + PropsValues.FREEMARKER_ENGINE_RESTRICTED_PACKAGES) { + + if (className.startsWith(restrictedPackageName)) { + throw new TemplateException( + "Instantiating " + className + " is not allowed in the " + + "template for security reasons", + environment); + } + } + + try { + return Class.forName( + className, true, PACLClassLoaderUtil.getContextClassLoader()); + } + catch (Exception e) { + throw new TemplateException(e, environment); + } + } + +} \ No newline at end of file diff --git a/portal-impl/src/com/liferay/portal/freemarker/PACLFreeMarkerTemplate.java b/portal-impl/src/com/liferay/portal/freemarker/PACLFreeMarkerTemplate.java new file mode 100644 index 00000000000000..609a990f870469 --- /dev/null +++ b/portal-impl/src/com/liferay/portal/freemarker/PACLFreeMarkerTemplate.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + */ + +package com.liferay.portal.freemarker; + +import com.liferay.portal.kernel.template.TemplateException; +import com.liferay.portal.security.lang.PortalSecurityManagerThreadLocal; +import com.liferay.portal.security.pacl.PACLPolicy; +import com.liferay.portal.template.TemplateContextHelper; + +import freemarker.template.Configuration; + +import java.io.Writer; + +import java.util.Map; + +/** + * @author Raymond Augé + */ +public class PACLFreeMarkerTemplate extends FreeMarkerTemplate { + + public PACLFreeMarkerTemplate( + String templateId, String templateContent, String errorTemplateId, + String errorTemplateContent, Map context, + Configuration configuration, + TemplateContextHelper templateContextHelper, + StringTemplateLoader stringTemplateLoader, PACLPolicy paclPolicy) { + + super( + templateId, templateContent, errorTemplateId, errorTemplateContent, + context, configuration, templateContextHelper, + stringTemplateLoader); + + _paclPolicy = paclPolicy; + } + + @Override + public boolean processTemplate(Writer writer) throws TemplateException { + PACLPolicy initialPolicy = + PortalSecurityManagerThreadLocal.getPACLPolicy(); + + try { + PortalSecurityManagerThreadLocal.setPACLPolicy(_paclPolicy); + + return super.processTemplate(writer); + } + finally { + PortalSecurityManagerThreadLocal.setPACLPolicy(initialPolicy); + } + } + + private PACLPolicy _paclPolicy; + +} \ No newline at end of file diff --git a/portal-impl/src/com/liferay/portal/template/TemplateContextHelper.java b/portal-impl/src/com/liferay/portal/template/TemplateContextHelper.java index 8fc4e97c6ecf76..0fc32683ce6240 100644 --- a/portal-impl/src/com/liferay/portal/template/TemplateContextHelper.java +++ b/portal-impl/src/com/liferay/portal/template/TemplateContextHelper.java @@ -19,6 +19,8 @@ import com.liferay.portal.kernel.json.JSONFactoryUtil; import com.liferay.portal.kernel.language.LanguageUtil; import com.liferay.portal.kernel.language.UnicodeLanguageUtil; +import com.liferay.portal.kernel.log.Log; +import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.servlet.BrowserSnifferUtil; import com.liferay.portal.kernel.templateparser.TemplateContext; import com.liferay.portal.kernel.util.ArrayUtil_IW; @@ -100,23 +102,44 @@ public Map getHelperUtilities() { // Audit message factory - variables.put( - "auditMessageFactoryUtil", - AuditMessageFactoryUtil.getAuditMessageFactory()); + try { + variables.put( + "auditMessageFactoryUtil", + AuditMessageFactoryUtil.getAuditMessageFactory()); + } + catch (SecurityException se) { + _log.error(se, se); + } // Audit router util - variables.put("auditRouterUtil", AuditRouterUtil.getAuditRouter()); + try { + variables.put("auditRouterUtil", AuditRouterUtil.getAuditRouter()); + } + catch (SecurityException se) { + _log.error(se, se); + } // Browser sniffer - variables.put("browserSniffer", BrowserSnifferUtil.getBrowserSniffer()); + try { + variables.put( + "browserSniffer", BrowserSnifferUtil.getBrowserSniffer()); + } + catch (SecurityException se) { + _log.error(se, se); + } // Date format - variables.put( - "dateFormatFactory", - FastDateFormatFactoryUtil.getFastDateFormatFactory()); + try { + variables.put( + "dateFormatFactory", + FastDateFormatFactoryUtil.getFastDateFormatFactory()); + } + catch (SecurityException se) { + _log.error(se, se); + } // Date util @@ -124,32 +147,62 @@ public Map getHelperUtilities() { // Expando column service - ServiceLocator serviceLocator = ServiceLocator.getInstance(); + try { + ServiceLocator serviceLocator = ServiceLocator.getInstance(); + + // Service locator - variables.put( - "expandoColumnLocalService", - serviceLocator.findService( - ExpandoColumnLocalService.class.getName())); + variables.put("serviceLocator", serviceLocator); + + try { + variables.put( + "expandoColumnLocalService", + serviceLocator.findService( + ExpandoColumnLocalService.class.getName())); + } + catch (SecurityException se) { + _log.error(se, se); + } - // Expando row service + // Expando row service - variables.put( - "expandoRowLocalService", - serviceLocator.findService(ExpandoRowLocalService.class.getName())); + try { + variables.put( + "expandoRowLocalService", + serviceLocator.findService( + ExpandoRowLocalService.class.getName())); + } + catch (SecurityException se) { + _log.error(se, se); + } - // Expando table service + // Expando table service - variables.put( - "expandoTableLocalService", - serviceLocator.findService( - ExpandoTableLocalService.class.getName())); + try { + variables.put( + "expandoTableLocalService", + serviceLocator.findService( + ExpandoTableLocalService.class.getName())); + } + catch (SecurityException se) { + _log.error(se, se); + } - // Expando value service + // Expando value service - variables.put( - "expandoValueLocalService", - serviceLocator.findService( - ExpandoValueLocalService.class.getName())); + try { + variables.put( + "expandoValueLocalService", + serviceLocator.findService( + ExpandoValueLocalService.class.getName())); + } + catch (SecurityException se) { + _log.error(se, se); + } + } + catch (SecurityException se) { + _log.error(se, se); + } // Getter util @@ -157,31 +210,67 @@ public Map getHelperUtilities() { // Html util - variables.put("htmlUtil", HtmlUtil.getHtml()); + try { + variables.put("htmlUtil", HtmlUtil.getHtml()); + } + catch (SecurityException se) { + _log.error(se, se); + } // Http util - variables.put("httpUtil", HttpUtil.getHttp()); + try { + variables.put("httpUtil", HttpUtil.getHttp()); + } + catch (SecurityException se) { + _log.error(se, se); + } // Journal content util - variables.put( - "journalContentUtil", JournalContentUtil.getJournalContent()); + try { + variables.put( + "journalContentUtil", JournalContentUtil.getJournalContent()); + } + catch (SecurityException se) { + _log.error(se, se); + } // JSON factory util - variables.put("jsonFactoryUtil", JSONFactoryUtil.getJSONFactory()); + try { + variables.put("jsonFactoryUtil", JSONFactoryUtil.getJSONFactory()); + } + catch (SecurityException se) { + _log.error(se, se); + } // Language util - variables.put("languageUtil", LanguageUtil.getLanguage()); + try { + variables.put("languageUtil", LanguageUtil.getLanguage()); + } + catch (SecurityException se) { + _log.error(se, se); + } - variables.put( - "unicodeLanguageUtil", UnicodeLanguageUtil.getUnicodeLanguage()); + try { + variables.put( + "unicodeLanguageUtil", + UnicodeLanguageUtil.getUnicodeLanguage()); + } + catch (SecurityException se) { + _log.error(se, se); + } // Locale util - variables.put("localeUtil", LocaleUtil.getInstance()); + try { + variables.put("localeUtil", LocaleUtil.getInstance()); + } + catch (SecurityException se) { + _log.error(se, se); + } // Param util @@ -189,38 +278,80 @@ public Map getHelperUtilities() { // Portal util - variables.put("portalUtil", PortalUtil.getPortal()); + try { + variables.put("portalUtil", PortalUtil.getPortal()); + } + catch (SecurityException se) { + _log.error(se, se); + } - variables.put("portal", PortalUtil.getPortal()); + try { + variables.put("portal", PortalUtil.getPortal()); + } + catch (SecurityException se) { + _log.error(se, se); + } // Prefs props util - variables.put("prefsPropsUtil", PrefsPropsUtil.getPrefsProps()); + try { + variables.put("prefsPropsUtil", PrefsPropsUtil.getPrefsProps()); + } + catch (SecurityException se) { + _log.error(se, se); + } // Props util - variables.put("propsUtil", PropsUtil.getProps()); + try { + variables.put("propsUtil", PropsUtil.getProps()); + } + catch (SecurityException se) { + _log.error(se, se); + } // Portlet URL factory - variables.put( - "portletURLFactory", PortletURLFactoryUtil.getPortletURLFactory()); + try { + variables.put( + "portletURLFactory", + PortletURLFactoryUtil.getPortletURLFactory()); + } + catch (SecurityException se) { + _log.error(se, se); + } // Randomizer - variables.put( - "randomizer", Randomizer_IW.getInstance().getWrappedInstance()); + try { + variables.put( + "randomizer", Randomizer_IW.getInstance().getWrappedInstance()); + } + catch (SecurityException se) { + _log.error(se, se); + } - // SAX reader util + try { + UtilLocator utilLocator = UtilLocator.getInstance(); - UtilLocator utilLocator = UtilLocator.getInstance(); + // Util locator - variables.put( - "saxReaderUtil", utilLocator.findUtil(SAXReader.class.getName())); + variables.put("utilLocator", utilLocator); - // Service locator + // SAX reader util - variables.put("serviceLocator", serviceLocator); + try { + variables.put( + "saxReaderUtil", + utilLocator.findUtil(SAXReader.class.getName())); + } + catch (SecurityException se) { + _log.error(se, se); + } + } + catch (SecurityException se) { + _log.error(se, se); + } // Session clicks @@ -238,10 +369,6 @@ public Map getHelperUtilities() { variables.put("timeZoneUtil", TimeZoneUtil_IW.getInstance()); - // Util locator - - variables.put("utilLocator", utilLocator); - // Unicode formatter variables.put("unicodeFormatter", UnicodeFormatter_IW.getInstance()); @@ -252,46 +379,138 @@ public Map getHelperUtilities() { // Web server servlet token - variables.put( - "webServerToken", - WebServerServletTokenUtil.getWebServerServletToken()); + try { + variables.put( + "webServerToken", + WebServerServletTokenUtil.getWebServerServletToken()); + } + catch (SecurityException se) { + _log.error(se, se); + } // Permissions - variables.put( - "accountPermission", AccountPermissionUtil.getAccountPermission()); - variables.put( - "commonPermission", CommonPermissionUtil.getCommonPermission()); - variables.put( - "groupPermission", GroupPermissionUtil.getGroupPermission()); - variables.put( - "layoutPermission", LayoutPermissionUtil.getLayoutPermission()); - variables.put( - "organizationPermission", - OrganizationPermissionUtil.getOrganizationPermission()); - variables.put( - "passwordPolicyPermission", - PasswordPolicyPermissionUtil.getPasswordPolicyPermission()); - variables.put( - "portalPermission", PortalPermissionUtil.getPortalPermission()); - variables.put( - "portletPermission", PortletPermissionUtil.getPortletPermission()); - variables.put("rolePermission", RolePermissionUtil.getRolePermission()); - variables.put( - "userGroupPermission", - UserGroupPermissionUtil.getUserGroupPermission()); - variables.put("userPermission", UserPermissionUtil.getUserPermission()); + try { + variables.put( + "accountPermission", + AccountPermissionUtil.getAccountPermission()); + } + catch (SecurityException se) { + _log.error(se, se); + } + + try { + variables.put( + "commonPermission", CommonPermissionUtil.getCommonPermission()); + } + catch (SecurityException se) { + _log.error(se, se); + } + + try { + variables.put( + "groupPermission", GroupPermissionUtil.getGroupPermission()); + } + catch (SecurityException se) { + _log.error(se, se); + } + + try { + variables.put( + "layoutPermission", LayoutPermissionUtil.getLayoutPermission()); + } + catch (SecurityException se) { + _log.error(se, se); + } + + try { + variables.put( + "organizationPermission", + OrganizationPermissionUtil.getOrganizationPermission()); + } + catch (SecurityException se) { + _log.error(se, se); + } + + try { + variables.put( + "passwordPolicyPermission", + PasswordPolicyPermissionUtil.getPasswordPolicyPermission()); + } + catch (SecurityException se) { + _log.error(se, se); + } + + try { + variables.put( + "portalPermission", PortalPermissionUtil.getPortalPermission()); + } + catch (SecurityException se) { + _log.error(se, se); + } + + try { + variables.put( + "portletPermission", + PortletPermissionUtil.getPortletPermission()); + } + catch (SecurityException se) { + _log.error(se, se); + } + + try { + variables.put( + "rolePermission", RolePermissionUtil.getRolePermission()); + } + catch (SecurityException se) { + _log.error(se, se); + } + + try { + variables.put( + "userGroupPermission", + UserGroupPermissionUtil.getUserGroupPermission()); + } + catch (SecurityException se) { + _log.error(se, se); + } + + try { + variables.put( + "userPermission", UserPermissionUtil.getUserPermission()); + } + catch (SecurityException se) { + _log.error(se, se); + } // Deprecated - variables.put( - "dateFormats", - FastDateFormatFactoryUtil.getFastDateFormatFactory()); - variables.put( - "imageToken", WebServerServletTokenUtil.getWebServerServletToken()); - variables.put( - "locationPermission", - OrganizationPermissionUtil.getOrganizationPermission()); + try { + variables.put( + "dateFormats", + FastDateFormatFactoryUtil.getFastDateFormatFactory()); + } + catch (SecurityException se) { + _log.error(se, se); + } + + try { + variables.put( + "imageToken", + WebServerServletTokenUtil.getWebServerServletToken()); + } + catch (SecurityException se) { + _log.error(se, se); + } + + try { + variables.put( + "locationPermission", + OrganizationPermissionUtil.getOrganizationPermission()); + } + catch (SecurityException se) { + _log.error(se, se); + } return variables; } @@ -486,4 +705,7 @@ protected void prepareTiles( templateContext.put("tilesSelectable", tilesSelectable); } + private static Log _log = LogFactoryUtil.getLog( + TemplateContextHelper.class); + } \ No newline at end of file diff --git a/portal-impl/src/com/liferay/portal/util/PropsValues.java b/portal-impl/src/com/liferay/portal/util/PropsValues.java index 051d5b5c7728f1..e8a1a63f64a520 100644 --- a/portal-impl/src/com/liferay/portal/util/PropsValues.java +++ b/portal-impl/src/com/liferay/portal/util/PropsValues.java @@ -608,6 +608,10 @@ public class PropsValues { public static final int FREEMARKER_ENGINE_MODIFICATION_CHECK_INTERVAL = GetterUtil.getInteger(PropsUtil.get(PropsKeys.FREEMARKER_ENGINE_MODIFICATION_CHECK_INTERVAL)); + public static final String[] FREEMARKER_ENGINE_RESTRICTED_CLASSES = PropsUtil.getArray(PropsKeys.FREEMARKER_ENGINE_RESTRICTED_CLASSES); + + public static final String[] FREEMARKER_ENGINE_RESTRICTED_PACKAGES = PropsUtil.getArray(PropsKeys.FREEMARKER_ENGINE_RESTRICTED_PACKAGES); + public static final String FREEMARKER_ENGINE_TEMPLATE_EXCEPTION_HANDLER = PropsUtil.get(PropsKeys.FREEMARKER_ENGINE_TEMPLATE_EXCEPTION_HANDLER); public static final String[] FREEMARKER_ENGINE_TEMPLATE_LOADERS = PropsUtil.getArray(PropsKeys.FREEMARKER_ENGINE_TEMPLATE_LOADERS); @@ -1644,6 +1648,10 @@ public class PropsValues { public static final int VELOCITY_ENGINE_RESOURCE_MANAGER_MODIFICATION_CHECK_INTERVAL = GetterUtil.getInteger(PropsUtil.get(PropsKeys.VELOCITY_ENGINE_RESOURCE_MANAGER_MODIFICATION_CHECK_INTERVAL)); + public static final String[] VELOCITY_ENGINE_RESTRICTED_CLASSES = PropsUtil.getArray(PropsKeys.VELOCITY_ENGINE_RESTRICTED_CLASSES); + + public static final String[] VELOCITY_ENGINE_RESTRICTED_PACKAGES = PropsUtil.getArray(PropsKeys.VELOCITY_ENGINE_RESTRICTED_PACKAGES); + public static final String VIRTUAL_HOSTS_DEFAULT_SITE_NAME = PropsUtil.get(PropsKeys.VIRTUAL_HOSTS_DEFAULT_SITE_NAME); public static final String[] VIRTUAL_HOSTS_IGNORE_EXTENSIONS = PropsUtil.getArray(PropsKeys.VIRTUAL_HOSTS_IGNORE_EXTENSIONS); diff --git a/portal-impl/src/com/liferay/portal/velocity/LiferayMethodExceptionEventHandler.java b/portal-impl/src/com/liferay/portal/velocity/LiferayMethodExceptionEventHandler.java new file mode 100644 index 00000000000000..9c68e27d4168da --- /dev/null +++ b/portal-impl/src/com/liferay/portal/velocity/LiferayMethodExceptionEventHandler.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + */ + +package com.liferay.portal.velocity; + +import com.liferay.portal.kernel.log.Log; +import com.liferay.portal.kernel.log.LogFactoryUtil; + +import org.apache.velocity.app.event.MethodExceptionEventHandler; + +/** + * @author Raymond Augé + */ +public class LiferayMethodExceptionEventHandler + implements MethodExceptionEventHandler { + + public Object methodException( + @SuppressWarnings("rawtypes") Class clazz, String method, + Exception e) + throws Exception { + + _log.error(e, e); + + return null; + } + + private static Log _log = LogFactoryUtil.getLog( + LiferayMethodExceptionEventHandler.class); + +} \ No newline at end of file diff --git a/portal-impl/src/com/liferay/portal/velocity/PACLVelocityTemplate.java b/portal-impl/src/com/liferay/portal/velocity/PACLVelocityTemplate.java new file mode 100644 index 00000000000000..d390bdca158d87 --- /dev/null +++ b/portal-impl/src/com/liferay/portal/velocity/PACLVelocityTemplate.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + */ + +package com.liferay.portal.velocity; + +import com.liferay.portal.kernel.template.TemplateException; +import com.liferay.portal.security.lang.PortalSecurityManagerThreadLocal; +import com.liferay.portal.security.pacl.PACLPolicy; +import com.liferay.portal.template.TemplateContextHelper; + +import java.io.Writer; + +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.VelocityEngine; + +/** + * @author Raymond Augé + */ +public class PACLVelocityTemplate extends VelocityTemplate { + + public PACLVelocityTemplate( + String templateId, String templateContent, String errorTemplateId, + String errorTemplateContent, VelocityContext velocityContext, + VelocityEngine velocityEngine, + TemplateContextHelper templateContextHelper, PACLPolicy paclPolicy) { + + super( + templateId, templateContent, errorTemplateId, errorTemplateContent, + velocityContext, velocityEngine, templateContextHelper); + + _paclPolicy = paclPolicy; + } + + @Override + public boolean processTemplate(Writer writer) throws TemplateException { + PACLPolicy initialPolicy = + PortalSecurityManagerThreadLocal.getPACLPolicy(); + + try { + PortalSecurityManagerThreadLocal.setPACLPolicy(_paclPolicy); + + return super.processTemplate(writer); + } + finally { + PortalSecurityManagerThreadLocal.setPACLPolicy(initialPolicy); + } + } + + private PACLPolicy _paclPolicy; + +} \ No newline at end of file diff --git a/portal-impl/src/com/liferay/portal/velocity/VelocityManager.java b/portal-impl/src/com/liferay/portal/velocity/VelocityManager.java index 5a10163d7bf3fb..274e5b8946a79b 100644 --- a/portal-impl/src/com/liferay/portal/velocity/VelocityManager.java +++ b/portal-impl/src/com/liferay/portal/velocity/VelocityManager.java @@ -19,18 +19,26 @@ import com.liferay.portal.kernel.template.TemplateException; import com.liferay.portal.kernel.template.TemplateManager; import com.liferay.portal.kernel.util.PropsKeys; +import com.liferay.portal.kernel.util.StringUtil; +import com.liferay.portal.security.lang.PortalSecurityManagerThreadLocal; +import com.liferay.portal.security.pacl.PACLClassLoaderUtil; +import com.liferay.portal.security.pacl.PACLPolicy; +import com.liferay.portal.security.pacl.PACLPolicyManager; import com.liferay.portal.template.RestrictedTemplate; import com.liferay.portal.template.TemplateContextHelper; import com.liferay.portal.util.PropsUtil; import com.liferay.portal.util.PropsValues; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.collections.ExtendedProperties; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.runtime.RuntimeConstants; import org.apache.velocity.runtime.resource.loader.StringResourceLoader; import org.apache.velocity.runtime.resource.util.StringResourceRepository; +import org.apache.velocity.util.introspection.SecureUberspector; /** * @author Raymond Augé @@ -65,17 +73,71 @@ public void clearCache(String templateId) { public void destroy() { StringResourceLoader.clearRepositories(); + _classLoaderVelocityContexts.clear(); + + _classLoaderVelocityContexts = null; _restrictedVelocityContext = null; _standardVelocityContext = null; _velocityEngine = null; _templateContextHelper = null; } + public void destroy(ClassLoader classLoader) { + _classLoaderVelocityContexts.remove(classLoader); + } + public Template getTemplate( String templateId, String templateContent, String errorTemplateId, String errorTemplateContent, TemplateContextType templateContextType) { - if (templateContextType.equals(TemplateContextType.EMPTY)) { + if (templateContextType.equals(TemplateContextType.CLASS_LOADER)) { + + // This template will have all of its utilities initialized within + // the class loader of the current thread + + ClassLoader contextClassLoader = + PACLClassLoaderUtil.getContextClassLoader(); + + PACLPolicy threadLocalPACLPolicy = + PortalSecurityManagerThreadLocal.getPACLPolicy(); + + PACLPolicy contextClassLoaderPACLPolicy = + PACLPolicyManager.getPACLPolicy(contextClassLoader); + + try { + PortalSecurityManagerThreadLocal.setPACLPolicy( + contextClassLoaderPACLPolicy); + + VelocityContext velocityContext = + _classLoaderVelocityContexts.get(contextClassLoader); + + if (velocityContext == null) { + velocityContext = new VelocityContext(); + + Map helperUtilities = + _templateContextHelper.getHelperUtilities(); + + for (Map.Entry entry : + helperUtilities.entrySet()) { + + velocityContext.put(entry.getKey(), entry.getValue()); + } + + _classLoaderVelocityContexts.put( + contextClassLoader, velocityContext); + } + + return new PACLVelocityTemplate( + templateId, templateContent, errorTemplateId, + errorTemplateContent, velocityContext, _velocityEngine, + _templateContextHelper, contextClassLoaderPACLPolicy); + } + finally { + PortalSecurityManagerThreadLocal.setPACLPolicy( + threadLocalPACLPolicy); + } + } + else if (templateContextType.equals(TemplateContextType.EMPTY)) { return new VelocityTemplate( templateId, templateContent, errorTemplateId, errorTemplateContent, null, _velocityEngine, @@ -142,30 +204,43 @@ public void init() throws TemplateException { ExtendedProperties extendedProperties = new FastExtendedProperties(); - extendedProperties.setProperty(_RESOURCE_LOADER, "string,servlet"); + extendedProperties.setProperty( + VelocityEngine.EVENTHANDLER_METHODEXCEPTION, + LiferayMethodExceptionEventHandler.class.getName()); extendedProperties.setProperty( - "string." + _RESOURCE_LOADER + ".cache", - String.valueOf( - PropsValues.VELOCITY_ENGINE_RESOURCE_MANAGER_CACHE_ENABLED)); + RuntimeConstants.INTROSPECTOR_RESTRICT_CLASSES, + StringUtil.merge(PropsValues.VELOCITY_ENGINE_RESTRICTED_CLASSES)); extendedProperties.setProperty( - "string." + _RESOURCE_LOADER + ".class", - StringResourceLoader.class.getName()); + RuntimeConstants.INTROSPECTOR_RESTRICT_PACKAGES, + StringUtil.merge(PropsValues.VELOCITY_ENGINE_RESTRICTED_PACKAGES)); extendedProperties.setProperty( - "string." + _RESOURCE_LOADER + ".repository.class", - StringResourceRepositoryImpl.class.getName()); + VelocityEngine.RESOURCE_LOADER, "string,servlet"); extendedProperties.setProperty( - "servlet." + _RESOURCE_LOADER + ".cache", + "servlet." + VelocityEngine.RESOURCE_LOADER + ".cache", String.valueOf( PropsValues.VELOCITY_ENGINE_RESOURCE_MANAGER_CACHE_ENABLED)); extendedProperties.setProperty( - "servlet." + _RESOURCE_LOADER + ".class", + "servlet." + VelocityEngine.RESOURCE_LOADER + ".class", LiferayResourceLoader.class.getName()); + extendedProperties.setProperty( + "string." + VelocityEngine.RESOURCE_LOADER + ".cache", + String.valueOf( + PropsValues.VELOCITY_ENGINE_RESOURCE_MANAGER_CACHE_ENABLED)); + + extendedProperties.setProperty( + "string." + VelocityEngine.RESOURCE_LOADER + ".class", + StringResourceLoader.class.getName()); + + extendedProperties.setProperty( + "string." + VelocityEngine.RESOURCE_LOADER + ".repository.class", + StringResourceRepositoryImpl.class.getName()); + extendedProperties.setProperty( VelocityEngine.RESOURCE_MANAGER_CLASS, PropsUtil.get(PropsKeys.VELOCITY_ENGINE_RESOURCE_MANAGER)); @@ -174,6 +249,18 @@ public void init() throws TemplateException { VelocityEngine.RESOURCE_MANAGER_CACHE_CLASS, PropsUtil.get(PropsKeys.VELOCITY_ENGINE_RESOURCE_MANAGER_CACHE)); + extendedProperties.setProperty( + VelocityEngine.RUNTIME_LOG_LOGSYSTEM_CLASS, + PropsUtil.get(PropsKeys.VELOCITY_ENGINE_LOGGER)); + + extendedProperties.setProperty( + VelocityEngine.RUNTIME_LOG_LOGSYSTEM + ".log4j.category", + PropsUtil.get(PropsKeys.VELOCITY_ENGINE_LOGGER_CATEGORY)); + + extendedProperties.setProperty( + RuntimeConstants.UBERSPECT_CLASSNAME, + SecureUberspector.class.getName()); + extendedProperties.setProperty( VelocityEngine.VM_LIBRARY, PropsUtil.get(PropsKeys.VELOCITY_ENGINE_VELOCIMACRO_LIBRARY)); @@ -188,14 +275,6 @@ public void init() throws TemplateException { String.valueOf( !PropsValues.VELOCITY_ENGINE_RESOURCE_MANAGER_CACHE_ENABLED)); - extendedProperties.setProperty( - VelocityEngine.RUNTIME_LOG_LOGSYSTEM_CLASS, - PropsUtil.get(PropsKeys.VELOCITY_ENGINE_LOGGER)); - - extendedProperties.setProperty( - VelocityEngine.RUNTIME_LOG_LOGSYSTEM + ".log4j.category", - PropsUtil.get(PropsKeys.VELOCITY_ENGINE_LOGGER_CATEGORY)); - _velocityEngine.setExtendedProperties(extendedProperties); try { @@ -229,9 +308,8 @@ public void setTemplateContextHelper( _templateContextHelper = templateContextHelper; } - private static final String _RESOURCE_LOADER = - VelocityEngine.RESOURCE_LOADER; - + private Map _classLoaderVelocityContexts = + new ConcurrentHashMap(); private VelocityContext _restrictedVelocityContext; private VelocityContext _standardVelocityContext; private TemplateContextHelper _templateContextHelper; diff --git a/portal-impl/src/com/liferay/portal/velocity/VelocityTemplateContextHelper.java b/portal-impl/src/com/liferay/portal/velocity/VelocityTemplateContextHelper.java index 08a5bb31bec17f..f9b2cb2ac8a7bb 100644 --- a/portal-impl/src/com/liferay/portal/velocity/VelocityTemplateContextHelper.java +++ b/portal-impl/src/com/liferay/portal/velocity/VelocityTemplateContextHelper.java @@ -14,6 +14,8 @@ package com.liferay.portal.velocity; +import com.liferay.portal.kernel.log.Log; +import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.templateparser.TemplateContext; import com.liferay.portal.kernel.util.GetterUtil; import com.liferay.portal.kernel.util.SetUtil; @@ -84,8 +86,13 @@ public Map getHelperUtilities() { // Permissions - velocityContext.put( - "rolePermission", RolePermissionUtil.getRolePermission()); + try { + velocityContext.put( + "rolePermission", RolePermissionUtil.getRolePermission()); + } + catch (SecurityException se) { + _log.error(se, se); + } return velocityContext; } @@ -159,4 +166,7 @@ public void prepare( } } + private static Log _log = LogFactoryUtil.getLog( + VelocityTemplateContextHelper.class); + } \ No newline at end of file diff --git a/portal-impl/src/portal.properties b/portal-impl/src/portal.properties index 32e60e62b1d460..f8e7b5eda4eb4a 100644 --- a/portal-impl/src/portal.properties +++ b/portal-impl/src/portal.properties @@ -5592,6 +5592,21 @@ freemarker.engine.localized.lookup=false freemarker.engine.modification.check.interval=60 + # + # Set a comma delimited list of java classes the FreeMarker engine cannot + # have access to. + # + freemarker.engine.restricted.classes=\ + java.lang.Class,\ + java.lang.ClassLoader,\ + java.lang.Thread + + # + # Set a comma delimited list of java packages the FreeMarker engine cannot + # have access to. + # + freemarker.engine.restricted.packages= + # # Exception handler can have it's value set to the name of a class # implementing FreeMarker TemplateExceptionHandler or rethrow, debug, @@ -6753,6 +6768,21 @@ velocity.engine.resource.manager.cache.enabled=true #velocity.engine.resource.manager.modification.check.interval=0 + # + # Set a comma delimited list of java classes the Velocity engine cannot have + # access to. + # + velocity.engine.restricted.classes=\ + java.lang.Class,\ + java.lang.ClassLoader,\ + java.lang.Thread + + # + # Set a comma delimited list of java packages the Velocity engine cannot + # have access to. + # + velocity.engine.restricted.packages= + # # Input a list of comma delimited macros that will be loaded. These files # must exist in the class path. diff --git a/portal-service/src/com/liferay/portal/kernel/template/TemplateManager.java b/portal-service/src/com/liferay/portal/kernel/template/TemplateManager.java index 3e6e561a15e64c..9b8b58b5460863 100644 --- a/portal-service/src/com/liferay/portal/kernel/template/TemplateManager.java +++ b/portal-service/src/com/liferay/portal/kernel/template/TemplateManager.java @@ -29,6 +29,8 @@ public interface TemplateManager { public void destroy(); + public void destroy(ClassLoader classLoader); + public Template getTemplate( String templateId, String templateContent, String errorTemplateId, String errorTemplateContent, TemplateContextType templateContextType); diff --git a/portal-service/src/com/liferay/portal/kernel/template/TemplateManagerUtil.java b/portal-service/src/com/liferay/portal/kernel/template/TemplateManagerUtil.java index 449355646c6305..f2bd1eca076e4a 100644 --- a/portal-service/src/com/liferay/portal/kernel/template/TemplateManagerUtil.java +++ b/portal-service/src/com/liferay/portal/kernel/template/TemplateManagerUtil.java @@ -56,6 +56,14 @@ public static void destroy() { templateManagers.clear(); } + public static void destroy(ClassLoader classLoader) { + Map templateManagers = _getTemplateManagers(); + + for (TemplateManager templateManager : templateManagers.values()) { + templateManager.destroy(classLoader); + } + } + public static Template getTemplate( String templateManagerName, String templateId, String templateContent, String errorTemplateId, diff --git a/portal-service/src/com/liferay/portal/kernel/util/PropsKeys.java b/portal-service/src/com/liferay/portal/kernel/util/PropsKeys.java index 9d937de53c759a..8d52927fa3741e 100644 --- a/portal-service/src/com/liferay/portal/kernel/util/PropsKeys.java +++ b/portal-service/src/com/liferay/portal/kernel/util/PropsKeys.java @@ -749,6 +749,10 @@ public interface PropsKeys { public static final String FREEMARKER_ENGINE_MODIFICATION_CHECK_INTERVAL = "freemarker.engine.modification.check.interval"; + public static final String FREEMARKER_ENGINE_RESTRICTED_CLASSES = "freemarker.engine.restricted.classes"; + + public static final String FREEMARKER_ENGINE_RESTRICTED_PACKAGES = "freemarker.engine.restricted.packages"; + public static final String FREEMARKER_ENGINE_TEMPLATE_EXCEPTION_HANDLER = "freemarker.engine.template.exception.handler"; public static final String FREEMARKER_ENGINE_TEMPLATE_LOADERS = "freemarker.engine.template.loaders"; @@ -2227,6 +2231,10 @@ public interface PropsKeys { public static final String VELOCITY_ENGINE_RESOURCE_MANAGER_MODIFICATION_CHECK_INTERVAL = "velocity.engine.resource.manager.modification.check.interval"; + public static final String VELOCITY_ENGINE_RESTRICTED_CLASSES = "velocity.engine.restricted.classes"; + + public static final String VELOCITY_ENGINE_RESTRICTED_PACKAGES = "velocity.engine.restricted.packages"; + public static final String VELOCITY_ENGINE_VELOCIMACRO_LIBRARY = "velocity.engine.velocimacro.library"; public static final String VERIFY_FREQUENCY = "verify.frequency";