/*************************************************************************
* *
* SignServer: The OpenSource Automated Signing Server *
* *
* This software 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 any later version. *
* *
* See terms of license at gnu.org. *
* *
*************************************************************************/
package org.signserver.module.statusproperties;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.*;
import javax.ejb.EJB;
import javax.naming.NamingException;
import org.apache.log4j.Logger;
import org.signserver.common.CryptoTokenOfflineException;
import org.signserver.common.GenericPropertiesRequest;
import org.signserver.common.GenericPropertiesResponse;
import org.signserver.common.GenericServletRequest;
import org.signserver.common.GenericServletResponse;
import org.signserver.common.GenericSignRequest;
import org.signserver.common.GenericSignResponse;
import org.signserver.common.IllegalRequestException;
import org.signserver.common.ProcessRequest;
import org.signserver.common.ProcessResponse;
import org.signserver.common.RequestContext;
import org.signserver.common.ServiceLocator;
import org.signserver.common.SignServerException;
import org.signserver.common.SignerStatus;
import org.signserver.server.cryptotokens.ICryptoToken;
import org.signserver.server.cryptotokens.NullCryptoToken;
import org.signserver.server.signers.BaseSigner;
import org.signserver.statusrepo.IStatusRepositorySession;
import org.signserver.statusrepo.common.NoSuchPropertyException;
import org.signserver.statusrepo.common.StatusEntry;
import org.signserver.statusrepo.common.StatusName;
/**
* Worker for setting and querying status properties.
*
* Worker properties:
* (none)
*
* The worker accepts a GenericPropertiesRequest or GenericSignRequest with
* properties in the request data.
*
* Request properties:
* GET - Comma-separated list of status properties to query
* x.VALUE - Where x is a status property: Sets the value of the property
* x.EXPIRATION - Where x is a status property: Sets the expiration time
* for x (x.VALUE must also be specified)
* If no property is specified all status properties are returned
*
* @author Markus KilÄs
* @version $Id: StatusPropertiesWorker.java 2978 2012-11-13 15:41:42Z netmackan $
* @see IStatusRepositorySession
* @see GenericPropertiesRequest
* @see GenericPropertiesResponse
*/
public class StatusPropertiesWorker extends BaseSigner {
/** Logger for this class. */
private static final Logger LOG = Logger.getLogger(StatusPropertiesWorker.class);
private static final String UPDATE = "UPDATE";
private static final String VALUE = "VALUE";
private static final String EXPIRATION = "EXPIRATION";
private static final ICryptoToken CRYPTO_TOKEN = new NullCryptoToken(SignerStatus.STATUS_ACTIVE);
/** StatusRepositorySession. */
@EJB
private IStatusRepositorySession.IRemote statusRepository;
private IStatusRepositorySession.IRemote getStatusRepository() {
if (statusRepository == null) {
try {
statusRepository = ServiceLocator.getInstance().lookupRemote(IStatusRepositorySession.IRemote.class);
} catch (NamingException ex) {
throw new RuntimeException("Unable to lookup worker session", ex);
}
}
return statusRepository;
}
@Override
public ProcessResponse processData(ProcessRequest request, RequestContext requestContext) throws IllegalRequestException, CryptoTokenOfflineException, SignServerException {
final ProcessResponse ret;
final Properties requestData, responseData;
// Check that the request contains a valid request
if (request instanceof GenericPropertiesRequest) {
requestData = ((GenericPropertiesRequest) request).getProperties();
} else if (request instanceof GenericSignRequest) {
requestData = new Properties();
try {
requestData.load(new ByteArrayInputStream(((GenericSignRequest) request).getRequestData()));
} catch (IOException ex) {
LOG.error("Error in request: " + requestContext.get(RequestContext.TRANSACTION_ID), ex);
throw new IllegalRequestException("Error parsing request. " + "See server log for information.");
}
} else {
throw new IllegalRequestException(
"Recieved request was not of expected type.");
}
// Process the request
responseData = process(requestData);
if (request instanceof GenericSignRequest) {
final GenericSignRequest signRequest = (GenericServletRequest) request;
try {
final ByteArrayOutputStream bout = new ByteArrayOutputStream();
responseData.store(bout, null);
if (request instanceof GenericServletRequest) {
ret = new GenericServletResponse(signRequest.getRequestID(),
bout.toByteArray(), null, null, null, "text/plain");
} else {
ret = new GenericSignResponse(signRequest.getRequestID(),
signRequest.getRequestData(), null, null, null, null);
}
} catch (IOException ex) {
LOG.error("Error constructing response for request: "
+ requestContext.get(RequestContext.TRANSACTION_ID),
ex);
throw new SignServerException("Error constructing response."
+ "See server log for information.");
}
} else {
ret = new GenericPropertiesResponse(responseData);
}
return ret;
}
private Properties process(Properties requestData) throws IllegalRequestException {
try {
Properties result = new Properties();
final Set gets;
if (requestData.isEmpty()) { // No request, just print every valid property
gets = EnumSet.allOf(StatusName.class);
} else {
// Add all requested names
gets = EnumSet.noneOf(StatusName.class);
String getValue = requestData.getProperty("GET");
if (getValue != null) {
for (String key : getValue.split(",|\\s")) {
try {
gets.add(StatusName.valueOf(key));
} catch (IllegalArgumentException ex) {
throw new IllegalRequestException("No such status property: " + key);
}
}
}
// Set values
for (Object k : requestData.keySet()) {
String key = (String) k;
if (key.endsWith("." + VALUE)) {
String name = key.substring(0, key.indexOf("." + VALUE));
String expiration = requestData.getProperty(name + "." + EXPIRATION);
try {
if (expiration == null) {
getStatusRepository().update(name, requestData.getProperty(key));
} else {
getStatusRepository().update(name, requestData.getProperty(key), Long.parseLong(expiration));
}
gets.add(StatusName.valueOf(name));
} catch (NumberFormatException ex) {
throw new IllegalRequestException("Illegal expiration value for property: " + name);
} catch (NoSuchPropertyException ex) {
throw new IllegalRequestException(ex.getMessage());
}
}
}
}
// Get the current values for the valid properties
for (StatusName get : gets) {
StatusEntry entry = getStatusRepository().getValidEntry(get.name());
if (entry != null) {
result.put(get.name() + "." + UPDATE, String.valueOf(entry.getUpdateTime()));
result.put(get.name() + "." + VALUE, entry.getValue() == null ? "" : String.valueOf(entry.getValue()));
result.put(get.name() + "." + EXPIRATION, String.valueOf(entry.getExpirationTime()));
}
}
return result;
} catch (NoSuchPropertyException ex) {
throw new RuntimeException(ex);
}
}
@Override
protected List getSignerCertificateFatalErrors() {
// This worker does not require any signer certificate so don't
// report any error about it.
return Collections.emptyList();
}
@Override
protected ICryptoToken getCryptoToken() throws SignServerException {
ICryptoToken result = super.getCryptoToken();
// Not configuring a crypto token for this worker is not a problem as
// this worker does not use a crypto token. Instead a dummy instance
// is returned.
if (result == null) {
result = CRYPTO_TOKEN;
}
return result;
}
}