/*
* Password Management Servlets (PWM)
* http://www.pwm-project.org
*
* Copyright (c) 2006-2009 Novell, Inc.
* Copyright (c) 2009-2017 The PWM Project
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package password.pwm.http.servlet.resource;
import com.google.gson.reflect.TypeToken;
import org.apache.commons.io.IOUtils;
import password.pwm.AppProperty;
import password.pwm.PwmApplication;
import password.pwm.config.Configuration;
import password.pwm.config.PwmSetting;
import password.pwm.config.value.FileValue;
import password.pwm.util.java.JsonUtil;
import password.pwm.util.logging.PwmLogger;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
class ResourceServletConfiguration {
private static final PwmLogger LOGGER = PwmLogger.forClass(ResourceServletConfiguration.class);
// settings with default values, values are set by app properties.
private int maxCacheItems = 100;
private long cacheExpireSeconds = 60;
private boolean enableGzip = false;
private boolean enablePathNonce = false;
private long maxCacheBytes = 1024;
private final Map<String, ZipFile> zipResources = new HashMap<>();
private final Map<String, FileResource> customFileBundle = new HashMap<>();
private Pattern noncePattern;
private String nonceValue;
private ResourceServletConfiguration() {
}
private ResourceServletConfiguration(final PwmApplication pwmApplication) {
LOGGER.trace("initializing");
final Configuration configuration = pwmApplication.getConfig();
maxCacheItems = Integer.parseInt(configuration.readAppProperty(AppProperty.HTTP_RESOURCES_MAX_CACHE_ITEMS));
cacheExpireSeconds = Long.parseLong(configuration.readAppProperty(AppProperty.HTTP_RESOURCES_EXPIRATION_SECONDS));
enableGzip = Boolean.parseBoolean(configuration.readAppProperty(AppProperty.HTTP_RESOURCES_ENABLE_GZIP));
enablePathNonce = Boolean.parseBoolean(configuration.readAppProperty(AppProperty.HTTP_RESOURCES_ENABLE_PATH_NONCE));
maxCacheBytes = Long.parseLong(configuration.readAppProperty(AppProperty.HTTP_RESOURCES_MAX_CACHE_BYTES));
final String noncePrefix = configuration.readAppProperty(AppProperty.HTTP_RESOURCES_NONCE_PATH_PREFIX);
noncePattern = Pattern.compile(noncePrefix + "[^/]*?/");
nonceValue = pwmApplication.getInstanceNonce();
final String zipFileResourceParam = configuration.readAppProperty(AppProperty.HTTP_RESOURCES_ZIP_FILES);
if (zipFileResourceParam != null && !zipFileResourceParam.isEmpty()) {
final List<ConfiguredZipFileResource> configuredZipFileResources = JsonUtil.deserialize(zipFileResourceParam, new TypeToken<ArrayList<ConfiguredZipFileResource>>() {
});
for (final ConfiguredZipFileResource configuredZipFileResource : configuredZipFileResources) {
final File webInfPath = pwmApplication.getPwmEnvironment().getContextManager().locateWebInfFilePath();
if (webInfPath != null) {
try {
final File zipFileFile = new File(
webInfPath.getParentFile() + "/"
+ ResourceFileServlet.RESOURCE_PATH
+ configuredZipFileResource.getZipFile()
);
final ZipFile zipFile = new ZipFile(zipFileFile);
zipResources.put(ResourceFileServlet.RESOURCE_PATH + configuredZipFileResource.getUrl(), zipFile);
LOGGER.debug("registered resource-zip file " + configuredZipFileResource.getZipFile() + " at path " + zipFileFile.getAbsolutePath());
} catch (IOException e) {
LOGGER.warn("unable to resource-zip file " + configuredZipFileResource + ", error: " + e.getMessage());
}
} else {
LOGGER.error("can't register resource-zip file " + configuredZipFileResource.getZipFile() + " because WEB-INF path is unknown");
}
}
}
final Map<FileValue.FileInformation, FileValue.FileContent> files = configuration.readSettingAsFile(PwmSetting.DISPLAY_CUSTOM_RESOURCE_BUNDLE);
if (files != null && !files.isEmpty()) {
final FileValue.FileInformation fileInformation = files.keySet().iterator().next();
final FileValue.FileContent fileContent = files.get(fileInformation);
LOGGER.debug("examining configured zip file resource for items name=" + fileInformation.getFilename() + ", size=" + fileContent.size());
try {
final Map<String, FileResource> customFiles = makeMemoryFileMapFromZipInput(fileContent.getContents());
customFileBundle.clear();
customFileBundle.putAll(customFiles);
} catch (IOException e) {
LOGGER.error("error assembling memory file map zip bundle: " + e.getMessage());
}
}
}
static ResourceServletConfiguration createResourceServletConfiguration(final PwmApplication pwmApplication) {
return new ResourceServletConfiguration(pwmApplication);
}
static ResourceServletConfiguration defaultConfiguration() {
return new ResourceServletConfiguration();
}
long getCacheExpireSeconds() {
return cacheExpireSeconds;
}
boolean isEnableGzip() {
return enableGzip;
}
boolean isEnablePathNonce() {
return enablePathNonce;
}
long getMaxCacheBytes() {
return maxCacheBytes;
}
Map<String, ZipFile> getZipResources() {
return zipResources;
}
Map<String, FileResource> getCustomFileBundle() {
return customFileBundle;
}
Pattern getNoncePattern() {
return noncePattern;
}
String getNonceValue() {
return nonceValue;
}
int getMaxCacheItems() {
return maxCacheItems;
}
private static Map<String, FileResource> makeMemoryFileMapFromZipInput(final byte[] content)
throws IOException
{
final ZipInputStream stream = new ZipInputStream(new ByteArrayInputStream(content));
final Map<String, FileResource> memoryMap = new HashMap<>();
ZipEntry entry;
while ((entry = stream.getNextEntry()) != null) {
if (!entry.isDirectory()) {
final String name = entry.getName();
final long lastModified = entry.getTime();
final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
IOUtils.copy(stream,byteArrayOutputStream);
final byte[] contents = byteArrayOutputStream.toByteArray();
memoryMap.put(name,new MemoryFileResource(name,contents,lastModified));
LOGGER.trace("discovered file in configured resource bundle: " + entry.getName());
}
}
return memoryMap;
}
private static class ConfiguredZipFileResource implements Serializable {
private String url;
private String zipFile;
public String getUrl() {
return url;
}
public void setUrl(final String url) {
this.url = url;
}
String getZipFile() {
return zipFile;
}
void setZipFile(final String zipFile) {
this.zipFile = zipFile;
}
}
}