/*
* 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.config.value;
import org.jdom2.Element;
import password.pwm.PwmConstants;
import password.pwm.config.PwmSetting;
import password.pwm.config.StoredValue;
import password.pwm.error.PwmOperationalException;
import password.pwm.error.PwmUnrecoverableException;
import password.pwm.util.java.JsonUtil;
import password.pwm.util.java.StringUtil;
import password.pwm.util.logging.PwmLogger;
import password.pwm.util.secure.PwmHashAlgorithm;
import password.pwm.util.secure.PwmSecurityKey;
import password.pwm.util.secure.SecureEngine;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public class FileValue extends AbstractValue implements StoredValue {
private static final PwmLogger LOGGER = PwmLogger.forClass(FileValue.class);
private Map<FileInformation, FileContent> values = new LinkedHashMap<>();
public static class FileInformation implements Serializable {
private String filename;
private String filetype;
public FileInformation(
final String filename,
final String filetype
)
{
this.filename = filename;
this.filetype = filetype;
}
public String getFilename()
{
return filename;
}
public String getFiletype()
{
return filetype;
}
}
public static class FileContent {
private byte[] contents;
public FileContent(final byte[] contents)
{
this.contents = contents;
}
public byte[] getContents()
{
return contents;
}
public static FileContent fromEncodedString(final String input)
throws IOException
{
final byte[] convertedBytes = StringUtil.base64Decode(input);
return new FileContent(convertedBytes);
}
public String toEncodedString()
throws IOException
{
return StringUtil.base64Encode(contents, StringUtil.Base64Options.GZIP);
}
public String md5sum()
throws PwmUnrecoverableException
{
return SecureEngine.hash(new ByteArrayInputStream(contents), PwmHashAlgorithm.MD5);
}
public String sha1sum()
throws PwmUnrecoverableException
{
return SecureEngine.hash(new ByteArrayInputStream(contents), PwmHashAlgorithm.SHA1);
}
public int size()
{
return contents.length;
}
}
public FileValue(final Map<FileInformation, FileContent> values)
{
this.values = values;
}
public static StoredValueFactory factory()
{
return new StoredValueFactory() {
public FileValue fromXmlElement(final Element settingElement, final PwmSecurityKey input)
throws PwmOperationalException
{
final List valueElements = settingElement.getChildren("value");
final Map<FileInformation, FileContent> values = new LinkedHashMap<>();
for (final Object loopValue : valueElements) {
final Element loopValueElement = (Element) loopValue;
final Element loopFileInformation = loopValueElement.getChild("FileInformation");
if (loopFileInformation != null) {
final String loopFileInformationJson = loopFileInformation.getText();
final FileInformation fileInformation = JsonUtil.deserialize(loopFileInformationJson,
FileInformation.class);
final Element loopFileContentElement = loopValueElement.getChild("FileContent");
if (loopFileContentElement != null) {
final String fileContentString = loopFileContentElement.getText();
final FileContent fileContent;
try {
fileContent = FileContent.fromEncodedString(fileContentString);
values.put(fileInformation, fileContent);
} catch (IOException e) {
LOGGER.error("error reading file contents item: " + e.getMessage(),e);
}
}
}
}
return new FileValue(values);
}
public StoredValue fromJson(final String input)
{
throw new IllegalStateException("not implemented");
}
};
}
public List<Element> toXmlValues(final String valueElementName)
{
final List<Element> returnList = new ArrayList<>();
for (final FileInformation fileInformation : values.keySet()) {
final Element valueElement = new Element(valueElementName);
final Element fileInformationElement = new Element("FileInformation");
fileInformationElement.addContent(JsonUtil.serialize(fileInformation));
valueElement.addContent(fileInformationElement);
final Element fileContentElement = new Element("FileContent");
final FileContent fileContent = values.get(fileInformation);
try {
fileContentElement.addContent(fileContent.toEncodedString());
} catch (IOException e) {
LOGGER.error("unexpected error writing setting to xml, IO error during base64 encoding: " + e.getMessage());
}
valueElement.addContent(fileContentElement);
returnList.add(valueElement);
}
return returnList;
}
@Override
public Object toNativeObject()
{
return values;
}
@Override
public List<String> validateValue(final PwmSetting pwm)
{
return Collections.emptyList();
}
@Override
public String toDebugString(
final Locale locale
)
{
final List<Map<String, Object>> output = asMetaData();
return JsonUtil.serialize((Serializable)output, JsonUtil.Flag.PrettyPrint);
}
@Override
public Serializable toDebugJsonObject(final Locale locale) {
return (Serializable)asMetaData();
}
public List<Map<String, Object>> asMetaData()
{
final List<Map<String, Object>> output = new ArrayList<>();
for (final FileInformation fileInformation : values.keySet()) {
final FileContent fileContent = values.get(fileInformation);
final Map<String, Object> details = new LinkedHashMap<>();
details.put("name", fileInformation.getFilename());
details.put("type", fileInformation.getFiletype());
details.put("size", fileContent.size());
try {
details.put("md5sum", fileContent.md5sum());
} catch (PwmUnrecoverableException e) { /* noop */ }
output.add(details);
}
return output;
}
public List<FileInfo> toInfoMap() {
if (values == null) {
return Collections.emptyList();
}
final List<FileInfo> returnObj = new ArrayList<>();
for (final FileValue.FileInformation fileInformation : this.values.keySet()) {
final FileContent fileContent = this.values.get(fileInformation);
final FileInfo loopInfo = new FileInfo();
loopInfo.name = fileInformation.getFilename();
loopInfo.type = fileInformation.getFiletype();
loopInfo.size = fileContent.size();
try {
loopInfo.md5sum = fileContent.md5sum();
loopInfo.sha1sum = fileContent.sha1sum();
} catch (PwmUnrecoverableException e) {
LOGGER.warn("error generating hash for certificate: " + e.getMessage());
}
returnObj.add(loopInfo);
}
return Collections.unmodifiableList(returnObj);
}
@Override
public String valueHash() throws PwmUnrecoverableException {
return SecureEngine.hash(JsonUtil.serializeCollection(toInfoMap()), PwmConstants.SETTING_CHECKSUM_HASH_METHOD);
}
public static class FileInfo implements Serializable {
public String name;
public String type;
public int size;
public String md5sum;
public String sha1sum;
private FileInfo() {
}
public String getName() {
return name;
}
public String getType() {
return type;
}
public int getSize() {
return size;
}
public String getMd5sum() {
return md5sum;
}
public String getSha1sum() {
return sha1sum;
}
}
}