diff options
| author | mcentner <mcentner@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4> | 2008-08-29 12:11:34 +0000 | 
|---|---|---|
| committer | mcentner <mcentner@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4> | 2008-08-29 12:11:34 +0000 | 
| commit | 32d17447a258188b2d534bcb0bf65a659ba7b7d0 (patch) | |
| tree | 4ad8bb267eb29f7091a7da283f6d7eec1e2188e1 /bkucommon/src/main/java | |
| download | mocca-32d17447a258188b2d534bcb0bf65a659ba7b7d0.tar.gz mocca-32d17447a258188b2d534bcb0bf65a659ba7b7d0.tar.bz2 mocca-32d17447a258188b2d534bcb0bf65a659ba7b7d0.zip | |
Initial import.
git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@1 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4
Diffstat (limited to 'bkucommon/src/main/java')
79 files changed, 9339 insertions, 0 deletions
| diff --git a/bkucommon/src/main/java/META-INF/MANIFEST.MF b/bkucommon/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 00000000..5e949512 --- /dev/null +++ b/bkucommon/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0
 +Class-Path: 
 +
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/AbstractBindingProcessor.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/AbstractBindingProcessor.java new file mode 100644 index 00000000..17ce29ce --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/AbstractBindingProcessor.java @@ -0,0 +1,86 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +import java.io.InputStream;
 +import java.util.Date;
 +
 +import at.gv.egiz.bku.slcommands.SLCommandInvoker;
 +import at.gv.egiz.stal.STAL;
 +
 +public abstract class AbstractBindingProcessor implements BindingProcessor {
 +  protected Id id;
 +  protected STAL stal;
 +  protected SLCommandInvoker commandInvoker;
 +  protected long lastAccessedTime = System.currentTimeMillis();
 +
 +  public AbstractBindingProcessor(String idString) {
 +    this.id = IdFactory.getInstance().createId(idString);
 +  }
 +
 +  /**
 +   * @see java.lang.Thread#run()
 +   */
 +  public abstract void run();
 +
 +  /**
 +   * The caller is advised to check the result in case an error occurred.
 +   * 
 +   * @see #getResult()
 +   */
 +  public abstract void consumeRequestStream(InputStream aIs);
 +
 +  public Id getId() {
 +    return id;
 +  }
 +
 +  public STAL getSTAL() {
 +    return stal;
 +  }
 +
 +  public SLCommandInvoker getCommandInvoker() {
 +    return commandInvoker;
 +  }
 +  
 +  public void updateLastAccessTime() {
 +    lastAccessedTime = System.currentTimeMillis();
 +  }
 +
 +  public Date getLastAccessTime() {
 +    return new Date(lastAccessedTime);
 +  }
 +
 +  /**
 +   * To be called after object creation.
 +   * 
 +   * @param aStal
 +   *          must not be null
 +   * @param aCommandInvoker
 +   *          must not be null
 +   */
 +  public void init(STAL aStal, SLCommandInvoker aCommandInvoker) {
 +    if (aStal == null) {
 +      throw new NullPointerException("STAL must not be set to null");
 +    }
 +    if (aCommandInvoker == null) {
 +      throw new NullPointerException("Commandinvoker must not be set to null");
 +    }
 +    stal = aStal;
 +    commandInvoker = aCommandInvoker;
 +    Thread.currentThread().setName("BPID#"+getId().toString());
 +  }
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessor.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessor.java new file mode 100644 index 00000000..c386508d --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessor.java @@ -0,0 +1,75 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.io.OutputStream;
 +import java.util.Date;
 +import java.util.Locale;
 +
 +import at.gv.egiz.bku.slcommands.SLCommandInvoker;
 +import at.gv.egiz.stal.STAL;
 +
 +/**
 + * Represents an single instance of a SL HTTP binding.
 + * 
 + * @author wbauer
 + *
 + */
 +public interface BindingProcessor extends Runnable {
 +
 +  /**
 +   * The stream must be read completely within this method.
 +   * 
 +   * The caller is advised to check the result in case an error occurred.
 +   * 
 +   * @see #getResult()
 +   */
 +  public void consumeRequestStream(InputStream aIs);
 +
 +  /**
 +   * The unique Id of this http binding instance.
 +   * @return
 +   */
 +  public Id getId();
 +
 +  /**
 +   * The used underlying STAL instance
 +   * @return
 +   */
 +  public STAL getSTAL();
 +
 +  public SLCommandInvoker getCommandInvoker();
 +
 +  public Date getLastAccessTime();
 +  
 +  public void updateLastAccessTime();
 +  
 +  public String getResultContentType();
 +  
 +  public void writeResultTo(OutputStream os, String encoding) throws IOException;
 +
 +  public void init(STAL aStal, SLCommandInvoker aCommandInvoker);
 +  
 +  /**
 +  * Sets the preferred locale for userinteraction.
 +  * If the locale is not set the default locale will be used.
 +  * @param locale must not be null.
 +  */
 + public void setLocale(Locale locale);
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessorManager.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessorManager.java new file mode 100644 index 00000000..a4e5bd90 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessorManager.java @@ -0,0 +1,102 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +import java.util.Locale;
 +import java.util.Set;
 +
 +import at.gv.egiz.bku.slcommands.SLCommandInvoker;
 +import at.gv.egiz.stal.STALFactory;
 +
 +/**
 + * Central player that handles the protocol binding.
 + * 
 + * @author wbauer
 + * 
 + */
 +public interface BindingProcessorManager {
 +
 +  /**
 +   * FactoryMethod creating a new BindingProcessor object.
 +   * The created binding processor must be passed to the process method to execute.
 +   * 
 +   * @param protcol
 +   *          the transport binding protocol
 +   * @param aSessionId
 +   *          optional an external sessionId (e.g. http session) could be
 +   *          provided. This parameter may be null.
 +   * @param locale the locale used for user interaction, may be null
 +   */
 +  public BindingProcessor createBindingProcessor(String protcol,
 +      String aSessionId, Locale locale);
 +
 +  /**
 +   * FactoryMethod creating a new BindingProcessor object.
 +   * The created binding processor must be passed to the process method to execute.
 +   * 
 +   * @param protcol
 +   *          the transport binding protocol
 +   * @param aSessionId
 +   *          optional an external sessionId (e.g. http session) could be
 +   *          provided. This parameter may be null.
 +   */
 +  public BindingProcessor createBindingProcessor(String protcol,
 +      String aSessionId);
 +
 +  
 +  /**
 +   * Gets the binding processor with a certain id. The binding processor must be passed to the 
 +   * process method before it is managed and thus returned by this method.
 +   * @param aId must not be null
 +   * @return null if the binding processor was not "processed" before.
 +   */
 +  public BindingProcessor getBindingProcessor(Id aId);
 +
 +  /**
 +   * Sets the STAL factory that is used for creating STAL objects that are used by BindingProcessor objects.
 +   * For each new BindingProcessor a new STAL object is created.
 +   * @param aStalFactory the factory to be used. Must not be null.
 +   */
 +  public void setSTALFactory(STALFactory aStalFactory);
 +  
 +  /**
 +   * Sets the invoker to be used.
 +   * @param invoker
 +   */
 +  public void setSLCommandInvoker(SLCommandInvoker invoker);
 +
 +  /**
 +   * Schedules the provided binding processor for processing and immediately returns.
 +   * 
 +   * @param aBindingProcessor
 +   */
 +  public void process(BindingProcessor aBindingProcessor);
 +  
 +  /**
 +   * Removes a formerly added (by calling the process method) binding processor.
 +   * @param bindingProcessor must not be null
 +   */
 +  public void removeBindingProcessor(Id sessionId); 
 +  
 +  /**
 +   * A set of all managed binding processors.
 +   * @return
 +   */
 +  public Set<Id> getManagedIds();
 +      
 +  public void shutdown();
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessorManagerImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessorManagerImpl.java new file mode 100644 index 00000000..7a3b1bb9 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessorManagerImpl.java @@ -0,0 +1,258 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +import java.util.Collections;
 +import java.util.HashMap;
 +import java.util.HashSet;
 +import java.util.Iterator;
 +import java.util.Locale;
 +import java.util.Map;
 +import java.util.Set;
 +import java.util.concurrent.ExecutorService;
 +import java.util.concurrent.Executors;
 +import java.util.concurrent.Future;
 +
 +import org.apache.commons.logging.Log;
 +import org.apache.commons.logging.LogFactory;
 +
 +import at.gv.egiz.bku.slcommands.SLCommandInvoker;
 +import at.gv.egiz.bku.slexceptions.SLRuntimeException;
 +import at.gv.egiz.bku.utils.binding.Protocol;
 +import at.gv.egiz.stal.STAL;
 +import at.gv.egiz.stal.STALFactory;
 +
 +/**
 + * This class maintains all active BindingProcessor Objects. Currently, only
 + * HTTPBinding is supported.
 + */
 +public class BindingProcessorManagerImpl implements BindingProcessorManager {
 +
 +  public final static Protocol[] SUPPORTED_PROTOCOLS = { Protocol.HTTP,
 +      Protocol.HTTPS };
 +
 +  private static Log log = LogFactory.getLog(BindingProcessorManagerImpl.class);
 +
 +  private RemovalStrategy removalStrategy;
 +  private STALFactory stalFactory;
 +  private SLCommandInvoker commandInvokerClass;
 +  private ExecutorService executorService;
 +  private Map<Id, MapEntityWrapper> bindingProcessorMap = Collections
 +      .synchronizedMap(new HashMap<Id, MapEntityWrapper>());
 +
 +  /**
 +   * Container to hold a Future and Bindingprocessor object as map value.
 +   * @author wbauer
 +   * @see BindingProcessorManagerImpl#bindingProcessorMap
 +   */
 +  static class MapEntityWrapper {
 +    private Future<?> future;
 +    private BindingProcessor bindingProcessor;
 +
 +    public MapEntityWrapper(Future<?> future, BindingProcessor bindingProcessor) {
 +      if ((bindingProcessor == null) || (future == null)) {
 +        throw new NullPointerException("Argument must not be null");
 +      }
 +      this.bindingProcessor = bindingProcessor;
 +      this.future = future;
 +    }
 +
 +    public Future<?> getFuture() {
 +      return future;
 +    }
 +
 +    public BindingProcessor getBindingProcessor() {
 +      return bindingProcessor;
 +    }
 +
 +    public int hashCode() {
 +      return bindingProcessor.getId().hashCode();
 +    }
 +
 +    public boolean equals(Object other) {
 +      if (other instanceof MapEntityWrapper) {
 +        MapEntityWrapper o = (MapEntityWrapper) other;
 +        return (o.bindingProcessor.getId().equals(bindingProcessor.getId()));
 +      } else {
 +        return false;
 +      }
 +    }
 +  }
 +
 +  /**
 +   * 
 +   * @param fab
 +   *          must not be null
 +   * @param ci
 +   *          must not be null (prototype to generate new instances)
 +   */
 +  public BindingProcessorManagerImpl(STALFactory fab, SLCommandInvoker ci) {
 +    if (fab == null) {
 +      throw new NullPointerException("STALFactory must not be null");
 +    }
 +    stalFactory = fab;
 +    if (ci == null) {
 +      throw new NullPointerException("SLCommandInvoker must not be null");
 +    }
 +    commandInvokerClass = ci;
 +    executorService = Executors.newCachedThreadPool();
 +  }
 +
 +  /**
 +   * 
 +   * @return the STALFactory currently used. 
 +   */
 +  public STALFactory getStalFactory() {
 +    return stalFactory;
 +  }
 +
 +  /**
 +   * Sets the STALFactory to be used.
 +   * @param stalFactory
 +   */
 +  public void setStalFactory(STALFactory stalFactory) {
 +    this.stalFactory = stalFactory;
 +  }
 +
 +  /**
 +   * Could be used to setup a new executor service during application stratup.
 +   * @param executorService
 +   */
 +  public void setExecutorService(ExecutorService executorService) {
 +    this.executorService = executorService;
 +  }
 +
 +  public void setRemovalStrategy(RemovalStrategy aStrategy) {
 +    removalStrategy = aStrategy;
 +  }
 +
 +  public RemovalStrategy getRemovlaStrategy() {
 +    return removalStrategy;
 +  }
 +
 +  public void shutdown() {
 +    log.info("Shutting down the BindingProcessorManager");
 +    executorService.shutdown();
 +  }
 +
 +  /**
 +   * Uses the default locale
 +   */
 +  public BindingProcessor createBindingProcessor(String protocol,
 +      String aSessionId) {
 +    return createBindingProcessor(protocol, aSessionId, null);
 +  }
 +  
 +  /**
 +   * FactoryMethod creating a new BindingProcessor object.
 +   * 
 +   * @param protocol
 +   *          must not be null
 +   */
 +  public BindingProcessor createBindingProcessor(String protocol,
 +      String aSessionId, Locale locale) {
 +    String low = protocol.toLowerCase();
 +    Protocol proto = null;
 +    for (int i = 0; i < SUPPORTED_PROTOCOLS.length; i++) {
 +      if (SUPPORTED_PROTOCOLS[i].toString().equals(low)) {
 +        proto = SUPPORTED_PROTOCOLS[i];
 +        break;
 +      }
 +    }
 +    if (proto == null) {
 +      throw new UnsupportedOperationException();
 +    }
 +    BindingProcessor bindingProcessor = new HTTPBindingProcessor(aSessionId,
 +        commandInvokerClass.newInstance(), proto);
 +    STAL stal = stalFactory.createSTAL();
 +    bindingProcessor.init(stal, commandInvokerClass.newInstance());
 +    if (locale != null) {
 +      bindingProcessor.setLocale(locale);
 +      stal.setLocale(locale);
 +    }
 +    return bindingProcessor;
 +  }
 +
 +  /**
 +   * @return the bindingprocessor object for this id or null if no bindingprocessor was found.
 +   */
 +  public BindingProcessor getBindingProcessor(Id aId) {
 +    if (bindingProcessorMap.get(aId) != null) {
 +      return bindingProcessorMap.get(aId).getBindingProcessor();
 +    } else {
 +      return null;
 +    }
 +  }
 +
 +  /**
 +   * 
 +   */
 +  public void setSTALFactory(STALFactory aStalFactory) {
 +    if (aStalFactory == null) {
 +      throw new NullPointerException("Cannot set STALFactory to null");
 +    }
 +    stalFactory = aStalFactory;
 +  }
 +
 +  /**
 +   * Causes the BindingProcessorManager to manage the provided BindingProcessor
 +   * @param aBindingProcessor must not be null
 +   */
 +  public void process(BindingProcessor aBindingProcessor) {
 +    if (bindingProcessorMap.containsKey(aBindingProcessor.getId())) {
 +      log.fatal("Clashing ids, cannot process bindingprocessor with id:"
 +          + aBindingProcessor.getId());
 +      throw new SLRuntimeException(
 +          "Clashing ids, cannot process bindingprocessor with id:"
 +              + aBindingProcessor.getId());
 +    }
 +    Future<?> f = executorService.submit(aBindingProcessor);
 +    bindingProcessorMap.put(aBindingProcessor.getId(), new MapEntityWrapper(f,
 +        aBindingProcessor));
 +  }
 +
 +  @Override
 +  public void setSLCommandInvoker(SLCommandInvoker invoker) {
 +    commandInvokerClass = invoker;
 +  }
 +
 +  @Override
 +  public void removeBindingProcessor(Id sessionId) {
 +    MapEntityWrapper wrapper = bindingProcessorMap
 +        .get(sessionId);
 +    if (wrapper == null) {
 +      return;
 +    }
 +    Future<?> f = wrapper.getFuture();
 +    if (!f.isDone()) {
 +      f.cancel(true);
 +    }
 +    bindingProcessorMap.remove(sessionId);
 +  }
 +
 +  @Override
 +  public Set<Id> getManagedIds() {
 +    Set<Id> result = new HashSet<Id>();
 +    synchronized (bindingProcessorMap) {
 +      for (Iterator<Id> it = bindingProcessorMap.keySet().iterator(); it
 +          .hasNext();) {
 +        result.add(it.next());
 +      }
 +    }
 +    return result;
 +  }
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrl.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrl.java new file mode 100644 index 00000000..8eaeacbd --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrl.java @@ -0,0 +1,62 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +import java.net.MalformedURLException;
 +import java.net.URL;
 +
 +import org.apache.commons.logging.Log;
 +import org.apache.commons.logging.LogFactory;
 +
 +import at.gv.egiz.bku.slexceptions.SLRuntimeException;
 +
 +/**
 + * Used to handle DataUrl connections as specified in the CCE's HTTP protocol binding. 
 + *
 + */
 +public class DataUrl {
 +  private static DataUrlConnectionSPI defaultDataUrlConnection = new DataUrlConnectionImpl();
 +  private static Log log = LogFactory.getLog(DataUrl.class);
 +  
 +  private URL url;
 +
 +  /**
 +   * Sets the default DataUrlConnection implementation
 +   * @param aClass must not be null
 +   */
 +  public static void setDataUrlConnectionClass(DataUrlConnectionSPI dataUrlConnection) {
 +    if (dataUrlConnection == null) {
 +      throw new NullPointerException("Default dataurlconnection must not be set to null");
 +    }
 +    defaultDataUrlConnection = dataUrlConnection;
 +  }
 +
 +  public DataUrl(String aUrlString) throws MalformedURLException {
 +    url = new URL(aUrlString);
 +  }
 +
 +  public DataUrlConnection openConnection() {
 +    try {
 +      DataUrlConnectionSPI retVal = defaultDataUrlConnection.newInstance();
 +      retVal.init(url);
 +      return retVal;
 +    } catch (Exception e) {
 +      log.error(e);
 +      throw new SLRuntimeException("Cannot instantiate a dataurlconnection:",e);
 +    }
 +  }
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnection.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnection.java new file mode 100644 index 00000000..e6d5e075 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnection.java @@ -0,0 +1,79 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.net.SocketTimeoutException;
 +import java.security.cert.X509Certificate;
 +
 +import at.gv.egiz.bku.slcommands.SLResult;
 +
 +/**
 + * Transmit a security layer result to DataURL via HTTP POST, encoded as multipart/form-data. 
 + * The HTTP header user-agent is set to <em>citizen-card-environment/1.2 BKU2 1.0</em>.
 + * The form-parameter ResponseType is set to <em>HTTP-Security-Layer-RESPONSE</em>.
 + * All other headers/parameters are set by the caller.
 + * 
 + * @author clemens
 + */
 +public interface DataUrlConnection {
 +
 +    public static final String DEFAULT_USERAGENT = "citizen-card-environment/1.2 BKU2 1.0";
 +    public static final String FORMPARAM_RESPONSETYPE = "ResponseType";
 +    public static final String DEFAULT_RESPONSETYPE = "HTTP-Security-Layer-RESPONSE";
 +    public static final String FORMPARAM_XMLRESPONSE = "XMLResponse";
 +    public static final String FORMPARAM_BINARYRESPONSE = "BinaryResponse";
 +    
 +    public static final String XML_RESPONSE_ENCODING = "UTF-8";
 +
 +    public String getProtocol();
 +    
 +    /**
 +     * Set a HTTP Header.
 +     * @param key
 +     * @param value multiple values are assumed to have the correct formatting (comma-separated list)
 +     */
 +    public void setHTTPHeader(String key, String value);
 +
 +    /**
 +     * Set a form-parameter.
 +     * @param name
 +     * @param data
 +     * @param contentType may be null
 +     * @param charSet may be null
 +     * @param transferEncoding may be null
 +     */
 +    public void setHTTPFormParameter(String name, InputStream data, String contentType, String charSet, String transferEncoding);
 +
 +    /**
 +     * @pre httpHeaders != null
 +     * @throws java.net.SocketTimeoutException
 +     * @throws java.io.IOException
 +     */
 +    public void connect() throws SocketTimeoutException, IOException;
 +
 +    public X509Certificate getServerCertificate();
 +
 +    /**
 +     * @pre connection != null
 +     * @throws java.io.IOException
 +     */
 +    public void transmit(SLResult slResult) throws IOException;
 +
 +    public DataUrlResponse getResponse() throws IOException;
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionImpl.java new file mode 100644 index 00000000..134d765e --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionImpl.java @@ -0,0 +1,216 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +import java.io.FileOutputStream;
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.io.OutputStream;
 +import java.net.HttpURLConnection;
 +import java.net.SocketTimeoutException;
 +import java.net.URL;
 +import java.security.cert.X509Certificate;
 +import java.util.ArrayList;
 +import java.util.HashMap;
 +import java.util.Iterator;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Set;
 +
 +import javax.net.ssl.HttpsURLConnection;
 +
 +import org.apache.commons.httpclient.methods.multipart.FilePart;
 +import org.apache.commons.httpclient.methods.multipart.Part;
 +import org.apache.commons.httpclient.methods.multipart.StringPart;
 +
 +import at.gv.egiz.bku.binding.multipart.InputStreamPartSource;
 +import at.gv.egiz.bku.binding.multipart.SLResultPart;
 +import at.gv.egiz.bku.slcommands.SLResult;
 +import at.gv.egiz.bku.slcommands.SLResult.SLResultType;
 +import at.gv.egiz.bku.slexceptions.SLRuntimeException;
 +import at.gv.egiz.bku.utils.StreamUtil;
 +import at.gv.egiz.bku.utils.binding.Protocol;
 +
 +/**
 + * not thread-safe thus newInsance always returns a new object
 + * 
 + */
 +public class DataUrlConnectionImpl implements DataUrlConnectionSPI {
 +
 +  public final static Protocol[] SUPPORTED_PROTOCOLS = { Protocol.HTTP,
 +      Protocol.HTTPS };
 +  protected X509Certificate serverCertificate;
 +  protected Protocol protocol;
 +  protected URL url;
 +  private HttpURLConnection connection;
 +  protected Map<String, String> requestHttpHeaders;
 +  protected ArrayList<Part> formParams;
 +  protected String boundary;
 +
 +  protected DataUrlResponse result;
 +
 +  public String getProtocol() {
 +    if (protocol == null) {
 +      return null;
 +    }
 +    return protocol.toString();
 +  }
 +
 +  /**
 +   * opens a connection sets the headers gets the server certificate
 +   * 
 +   * @throws java.net.SocketTimeoutException
 +   * @throws java.io.IOException
 +   * @pre url != null
 +   * @pre httpHeaders != null
 +   */
 +  public void connect() throws SocketTimeoutException, IOException {
 +    connection = (HttpURLConnection) url.openConnection();
 +
 +    // FIXXME move this to config.
 +    HttpURLConnection.setFollowRedirects(false);
 +    
 +    
 +    connection.setDoOutput(true);
 +    Set<String> headers = requestHttpHeaders.keySet();
 +    Iterator<String> headerIt = headers.iterator();
 +    while (headerIt.hasNext()) {
 +      String name = headerIt.next();
 +      connection.setRequestProperty(name, requestHttpHeaders.get(name));
 +    }
 +    connection.connect();
 +    if (connection instanceof HttpsURLConnection) {
 +      HttpsURLConnection ssl = (HttpsURLConnection) connection;
 +      X509Certificate[] certs = (X509Certificate[]) ssl.getServerCertificates();
 +      if ((certs != null) && (certs.length >= 1)) {
 +        serverCertificate = certs[0];
 +      }
 +    }
 +  }
 +
 +  public X509Certificate getServerCertificate() {
 +    return serverCertificate;
 +  }
 +
 +  public void setHTTPHeader(String name, String value) {
 +    if (name != null && value != null) {
 +      requestHttpHeaders.put(name, value);
 +    }
 +  }
 +
 +  public void setHTTPFormParameter(String name, InputStream data,
 +      String contentType, String charSet, String transferEncoding) {
 +    InputStreamPartSource source = new InputStreamPartSource(null, data);
 +    FilePart formParam = new FilePart(name, source, contentType, charSet);
 +    if (transferEncoding != null) {
 +      formParam.setTransferEncoding(transferEncoding);
 +    } else {
 +      formParam.setTransferEncoding(null);
 +    }
 +    formParams.add(formParam);
 +  }
 +
 +  /**
 +   * send all formParameters
 +   * 
 +   * @throws java.io.IOException
 +   */
 +  public void transmit(SLResult slResult) throws IOException {
 +    SLResultPart slResultPart = new SLResultPart(slResult,
 +        XML_RESPONSE_ENCODING);
 +    if (slResult.getResultType() == SLResultType.XML) {
 +      slResultPart.setTransferEncoding(null);
 +      slResultPart.setContentType(slResult.getMimeType());
 +      slResultPart.setCharSet(XML_RESPONSE_ENCODING);
 +    } else {
 +      slResultPart.setTransferEncoding(null);
 +      slResultPart.setContentType(slResult.getMimeType());
 +    }
 +    formParams.add(slResultPart);
 +
 +    OutputStream os = connection.getOutputStream();
 +    
 +    Part[] parts = new Part[formParams.size()];
 +    Part.sendParts(os, formParams.toArray(parts), boundary.getBytes());
 +    os.close();
 +    // MultipartRequestEntity PostMethod
 +    result = new DataUrlResponse(url.toString(), connection.getResponseCode(),
 +        connection.getInputStream());
 +    
 +    Map<String, String> responseHttpHeaders = new HashMap<String, String>();
 +    Map<String, List<String>> httpHeaders = connection.getHeaderFields();
 +    for (Iterator<String> keyIt = httpHeaders.keySet().iterator(); keyIt
 +        .hasNext();) {
 +      String key = keyIt.next();
 +      StringBuffer value = new StringBuffer();
 +      for (String val : httpHeaders.get(key)) {
 +        value.append(val);
 +        value.append(HttpUtil.SEPERATOR[0]);
 +      }
 +      String valString = value.substring(0, value.length() - 1);
 +      if ((key != null)&&(value.length() > 0)) {
 +        responseHttpHeaders.put(key, valString);
 +      }
 +    }
 +    result.setResponseHttpHeaders(responseHttpHeaders);
 +  }
 +
 +  @Override
 +  public DataUrlResponse getResponse() throws IOException {
 +    return result;
 +  }
 +
 +  /**
 +   * inits protocol, url, httpHeaders, formParams
 +   * 
 +   * @param url
 +   *          must not be null
 +   */
 +  @Override
 +  public void init(URL url) {
 +
 +    for (int i = 0; i < SUPPORTED_PROTOCOLS.length; i++) {
 +      if (SUPPORTED_PROTOCOLS[i].toString().equalsIgnoreCase(url.getProtocol())) {
 +        protocol = SUPPORTED_PROTOCOLS[i];
 +        break;
 +      }
 +    }
 +    if (protocol == null) {
 +      throw new SLRuntimeException("Protocol " + url.getProtocol()
 +          + " not supported for data url");
 +    }
 +    this.url = url;
 +    boundary = "--" + IdFactory.getInstance().createId().toString();
 +    requestHttpHeaders = new HashMap<String, String>();
 +    requestHttpHeaders.put(HttpUtil.HTTP_HEADER_USER_AGENT, DEFAULT_USERAGENT);
 +    requestHttpHeaders.put(HttpUtil.HTTP_HEADER_CONTENT_TYPE,
 +        HttpUtil.MULTIPART_FOTMDATA + HttpUtil.SEPERATOR[0]
 +            + HttpUtil.MULTIPART_FOTMDATA_BOUNDARY + "=" + boundary);
 +
 +    formParams = new ArrayList<Part>();
 +    StringPart responseType = new StringPart(FORMPARAM_RESPONSETYPE,
 +        DEFAULT_RESPONSETYPE);
 +    responseType.setCharSet("UTF-8");
 +    responseType.setTransferEncoding(null);
 +    formParams.add(responseType);
 +  }
 +
 +  @Override
 +  public DataUrlConnectionSPI newInstance() {
 +    return new DataUrlConnectionImpl();
 +  }
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionSPI.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionSPI.java new file mode 100644 index 00000000..9e5a66f8 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionSPI.java @@ -0,0 +1,42 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +import java.net.URL;
 +
 +/**
 + * Prototype of a DataurlconnectionSPI
 + * @author wbauer
 + *
 + */
 +public interface DataUrlConnectionSPI extends DataUrlConnection {
 +  
 +  /**
 +   * Returns a new instance of this class to handle a dataurl.
 +   * Called by the factory each time the openConnection method is called. 
 +   * @return
 +   */
 +  public DataUrlConnectionSPI newInstance();
 +  
 +  /**
 +   * Initializes the DataUrlConnection
 +   * @param url
 +   */
 +  public void init(URL url);
 +
 +
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlResponse.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlResponse.java new file mode 100644 index 00000000..b75cb0f3 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlResponse.java @@ -0,0 +1,98 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.io.PushbackInputStream;
 +import java.util.Iterator;
 +import java.util.Map;
 +
 +import at.gv.egiz.bku.utils.urldereferencer.StreamData;
 +
 +/**
 + * The response of a dataurl server.
 + * Additionally holds return code and response headers.
 + */
 +public class DataUrlResponse extends StreamData {
 +
 +  public final static String OK = "<ok/>";
 +
 +  protected Map<String, String> responseHttpHeaders;
 +
 +  protected int responseCode = -1;
 +
 +  public DataUrlResponse(String url, int responseCode, InputStream stream) {
 +    super(url, null, new PushbackInputStream(stream, 10));
 +    this.responseCode = responseCode;
 +  }
 +
 +  public String getContentType() {
 +    if (contentType != null) {
 +      return contentType;
 +    }
 +    if (responseHttpHeaders == null) {
 +      return null;
 +    }
 +    for (Iterator<String> keyIt = responseHttpHeaders.keySet().iterator(); keyIt
 +        .hasNext();) {
 +      String key = keyIt.next();
 +      if (HttpUtil.HTTP_HEADER_CONTENT_TYPE.equalsIgnoreCase(key)) {
 +        contentType = responseHttpHeaders.get(key);
 +        return contentType;
 +      }
 +    }
 +    return contentType;
 +  }
 +
 +  public void setResponseHttpHeaders(Map<String, String> responseHttpHeaders) {
 +    this.responseHttpHeaders = responseHttpHeaders;
 +  }
 +
 +  public Map<String, String> getResponseHeaders() {
 +    return responseHttpHeaders;
 +  }
 +
 +  public int getResponseCode() {
 +    return responseCode;
 +  }
 +
 +  /**
 +   * Checks if the http response equals "<ok/>"
 +   * 
 +   * @throws IOException
 +   */
 +  public boolean isHttpResponseXMLOK() throws IOException {
 +    String charset = HttpUtil.getCharset(contentType, true);
 +    byte[] buffer = new byte[10];
 +    int i = 0;
 +    int read = 0;
 +    while ((i < 10) && (read != -1)) {
 +      read = inputStream.read(buffer, i, 10 - i);
 +      if (read != -1) {
 +        i += read;
 +      }
 +    }
 +    PushbackInputStream pbis = (PushbackInputStream) inputStream;
 +    pbis.unread(buffer, 0, i);
 +    if (i < 5) {
 +      return false;
 +    }
 +    String ok = new String(buffer, 0, i, charset);
 +    return (OK.equals(ok));
 +  }
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/ExpiryRemover.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/ExpiryRemover.java new file mode 100644 index 00000000..d17a27c2 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/ExpiryRemover.java @@ -0,0 +1,67 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +import java.util.Iterator;
 +import java.util.Set;
 +
 +import org.apache.commons.logging.Log;
 +import org.apache.commons.logging.LogFactory;
 +
 +/**
 + * This class can be used to check the BindingProcessorManager for expired entries and remove them.
 + * Should be run periodically. 
 + *
 + */
 +public class ExpiryRemover implements RemovalStrategy {
 +
 +  private static Log log = LogFactory.getLog(ExpiryRemover.class);
 +
 +  protected BindingProcessorManager bindingProcessorManager;
 +  // keep max 5 min.
 +  protected long maxAcceptedAge = 1000 * 60 * 5;
 +
 +  @Override
 +  public void execute() {
 +    log.debug("Triggered Expiry Remover");
 +    if (bindingProcessorManager == null) {
 +      log.warn("Bindingprocessor not set, skipping removal");
 +      return;
 +    }
 +    Set<Id> managedIds = bindingProcessorManager.getManagedIds();
 +    for (Iterator<Id> it = managedIds.iterator(); it.hasNext();) {
 +      Id bindId = it.next();
 +      BindingProcessor bp = bindingProcessorManager.getBindingProcessor(bindId);
 +      if (bp != null) {
 +        if (bp.getLastAccessTime().getTime() < (System.currentTimeMillis() - maxAcceptedAge)) {
 +          log.debug("Removing binding processor: " + bp.getId());
 +          bindingProcessorManager.removeBindingProcessor(bp.getId());
 +        }
 +      }
 +    }
 +  }
 +
 +  public void setMaxAcceptedAge(long maxAcceptedAge) {
 +    this.maxAcceptedAge = maxAcceptedAge;
 +  }
 +
 +  @Override
 +  public void setBindingProcessorManager(BindingProcessorManager bp) {
 +    bindingProcessorManager = bp;
 +  }
 +
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/FixedFormParameters.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/FixedFormParameters.java new file mode 100644 index 00000000..cce3d720 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/FixedFormParameters.java @@ -0,0 +1,28 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +/**
 + * Form parameters with special meaning as defined in the CCE's http binding. 
 + *
 + */
 +public interface FixedFormParameters {
 +  String XMLREQUEST = "XMLRequest";
 +  String REDIRECTURL = "RedirectURL";
 +  String DATAURL = "DataURL";
 +  String STYLESHEETURL = "StylesheetURL";
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/FormParameter.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/FormParameter.java new file mode 100644 index 00000000..93339451 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/FormParameter.java @@ -0,0 +1,39 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +import java.io.InputStream;
 +import java.util.Iterator;
 +
 +/**
 + * Interface to access form control contents from the http request.
 + * It's designed to be used for URL encoded and multipart-formdata requests.
 + * @author wbauer
 + *
 + */
 +public interface FormParameter {
 +  
 +  String getFormParameterName();
 +
 +  InputStream getFormParameterValue();
 +
 +  String getFormParameterContentType();
 +  
 +  Iterator<String> getHeaderNames();
 +  
 +  String getHeaderValue(String headerName);
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/FormParameterImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/FormParameterImpl.java new file mode 100644 index 00000000..45aa9be6 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/FormParameterImpl.java @@ -0,0 +1,93 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +import java.io.InputStream;
 +import java.util.Collections;
 +import java.util.Iterator;
 +
 +import org.apache.commons.fileupload.FileItemHeaders;
 +
 +/**
 + * Simple wrapper to read data while consuming an stream within the http
 + * processor.
 + * 
 + * 
 + */
 +public class FormParameterImpl implements FormParameter {
 +
 +  protected InputStream dataStream;
 +  protected String contentType;
 +  protected String formName;
 +  protected FileItemHeaders headers;
 +
 +  public FormParameterImpl(String contentType, String formName, InputStream is,
 +      FileItemHeaders header) {
 +    this.contentType = contentType;
 +    this.formName = formName;
 +    this.dataStream = is;
 +    this.headers = header;
 +  }
 +
 +  @Override
 +  public String getFormParameterContentType() {
 +    return contentType;
 +  }
 +
 +  @Override
 +  public String getFormParameterName() {
 +    return formName;
 +  }
 +
 +  @Override
 +  public InputStream getFormParameterValue() {
 +    return dataStream;
 +  }
 +
 +  @Override
 +  public String getHeaderValue(String headerName) {
 +    if (headers == null) {
 +      return null;
 +    }
 +    return headers.getHeader(headerName);
 +  }
 +
 +  @SuppressWarnings("unchecked")
 +  @Override
 +  public Iterator<String> getHeaderNames() {
 +    if (headers == null) {
 +      return Collections.EMPTY_LIST.iterator();
 +    }
 +    return headers.getHeaderNames();
 +  }
 +
 +  public FileItemHeaders getHeaders() {
 +    return headers;
 +  }
 +
 +  public boolean equals(Object other) {
 +    if (other instanceof FormParameter) {
 +      FormParameter fp = (FormParameter) other;
 +      return fp.getFormParameterName().equals(getFormParameterName());
 +    }
 +    return false;
 +  }
 +  
 +  public int hashCode() {
 +    return getFormParameterName().hashCode();
 +  }
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/FormParameterStore.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/FormParameterStore.java new file mode 100644 index 00000000..8b6cd4b2 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/FormParameterStore.java @@ -0,0 +1,146 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +import java.io.ByteArrayInputStream;
 +import java.io.ByteArrayOutputStream;
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.util.Collections;
 +import java.util.Iterator;
 +
 +import org.apache.commons.fileupload.FileItemHeaders;
 +import org.apache.commons.fileupload.util.FileItemHeadersImpl;
 +
 +import at.gv.egiz.bku.slexceptions.SLRuntimeException;
 +import at.gv.egiz.bku.utils.StreamUtil;
 +
 +/**
 + * Simple store for form parameters based on a byte[]
 + * 
 + * @author wbauer
 + * 
 + */
 +public class FormParameterStore implements FormParameter {
 +
 +  private byte[] dataBuffer;
 +  private String contentType;
 +  private String parameterName;
 +  private boolean initialized = false;
 +  protected FileItemHeaders headers;
 +
 +  /**
 +   * Make sure to call init after creating a new instance.
 +   */
 +  public FormParameterStore() {
 +  }
 +
 +  public void init(InputStream dataSource, String paramName,
 +      String contentType, FileItemHeaders header) throws IOException {
 +    ByteArrayOutputStream os = new ByteArrayOutputStream();
 +    StreamUtil.copyStream(dataSource, os);
 +    this.dataBuffer = os.toByteArray();
 +    this.parameterName = paramName;
 +    this.contentType = contentType;
 +    initialized = true;
 +    this.headers = header;
 +  }
 +  
 +  public void init(byte[] dataSource, String paramName,
 +      String contentType, FileItemHeaders header) throws IOException {
 +    this.dataBuffer = dataSource;
 +    this.parameterName = paramName;
 +    this.contentType = contentType;
 +    initialized = true;
 +    this.headers = header;
 +  }
 +
 +  public void init(FormParameter fp) throws IOException {
 +    ByteArrayOutputStream os = new ByteArrayOutputStream();
 +    StreamUtil.copyStream(fp.getFormParameterValue(), os);
 +    this.dataBuffer = os.toByteArray();
 +    this.parameterName = fp.getFormParameterName();
 +    this.contentType = fp.getFormParameterContentType();
 +    if (fp instanceof FormParameterImpl) {
 +      headers = ((FormParameterImpl) fp).getHeaders();
 +    } else {
 +      FileItemHeadersImpl headersImpl = new FileItemHeadersImpl();
 +      for (Iterator<String> i = fp.getHeaderNames(); i.hasNext();) {
 +        String headerName = i.next();
 +        headersImpl.addHeader(headerName, fp.getHeaderValue(headerName));
 +      }
 +    }
 +    initialized = true;
 +  }
 +
 +  protected void ensureInitialized() {
 +    if (!initialized) {
 +      throw new SLRuntimeException("FormParameterStore not initialized");
 +    }
 +  }
 +
 +  /**
 +   * Reads all data from the stream and stores it internally. The stream will
 +   * not be closed.
 +   * 
 +   * @param datSource
 +   * @param formName
 +   * @param contentType
 +   */
 +  @Override
 +  public String getFormParameterContentType() {
 +    ensureInitialized();
 +    return contentType;
 +  }
 +
 +  @Override
 +  public String getFormParameterName() {
 +    ensureInitialized();
 +    return parameterName;
 +  }
 +
 +  /**
 +   * May be called more than once.
 +   */
 +  @Override
 +  public InputStream getFormParameterValue() {
 +    return new ByteArrayInputStream(dataBuffer);
 +  }
 +
 +  @Override
 +  public String getHeaderValue(String name) {
 +    if (headers == null) {
 +      return null;
 +    }
 +    return headers.getHeader(name);
 +  }
 +
 +  @SuppressWarnings("unchecked")
 +  @Override
 +  public Iterator<String> getHeaderNames() {
 +    if (headers == null) {
 +      return Collections.EMPTY_LIST.iterator();
 +    }
 +    return headers.getHeaderNames();
 +  }
 +  
 +  public boolean isEmpty() {
 +    ensureInitialized();
 +    return dataBuffer.length == 0;
 +  }
 +
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessor.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessor.java new file mode 100644 index 00000000..b79f7d55 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessor.java @@ -0,0 +1,820 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.io.InputStreamReader;
 +import java.io.OutputStream;
 +import java.io.OutputStreamWriter;
 +import java.io.Reader;
 +import java.security.cert.X509Certificate;
 +import java.util.ArrayList;
 +import java.util.Arrays;
 +import java.util.Collection;
 +import java.util.Collections;
 +import java.util.HashMap;
 +import java.util.Iterator;
 +import java.util.List;
 +import java.util.Locale;
 +import java.util.Map;
 +
 +import javax.net.ssl.SSLHandshakeException;
 +import javax.xml.transform.Transformer;
 +import javax.xml.transform.TransformerException;
 +import javax.xml.transform.TransformerFactory;
 +import javax.xml.transform.URIResolver;
 +import javax.xml.transform.stream.StreamResult;
 +import javax.xml.transform.stream.StreamSource;
 +
 +import org.apache.commons.logging.Log;
 +import org.apache.commons.logging.LogFactory;
 +
 +import at.gv.egiz.bku.slcommands.SLCommand;
 +import at.gv.egiz.bku.slcommands.SLCommandContext;
 +import at.gv.egiz.bku.slcommands.SLCommandFactory;
 +import at.gv.egiz.bku.slcommands.SLCommandInvoker;
 +import at.gv.egiz.bku.slcommands.SLResult;
 +import at.gv.egiz.bku.slcommands.SLSourceContext;
 +import at.gv.egiz.bku.slcommands.SLTargetContext;
 +import at.gv.egiz.bku.slcommands.impl.ErrorResultImpl;
 +import at.gv.egiz.bku.slexceptions.SLBindingException;
 +import at.gv.egiz.bku.slexceptions.SLCanceledException;
 +import at.gv.egiz.bku.slexceptions.SLException;
 +import at.gv.egiz.bku.slexceptions.SLRuntimeException;
 +import at.gv.egiz.bku.utils.StreamUtil;
 +import at.gv.egiz.bku.utils.binding.Protocol;
 +import at.gv.egiz.bku.utils.urldereferencer.FormDataURLSupplier;
 +import at.gv.egiz.bku.utils.urldereferencer.SimpleFormDataContextImpl;
 +import at.gv.egiz.bku.utils.urldereferencer.StreamData;
 +import at.gv.egiz.bku.utils.urldereferencer.URIResolverAdapter;
 +import at.gv.egiz.bku.utils.urldereferencer.URLDereferencer;
 +import at.gv.egiz.bku.utils.urldereferencer.URLDereferencerContext;
 +import at.gv.egiz.stal.QuitRequest;
 +import at.gv.egiz.stal.STALRequest;
 +
 +/**
 + * Class performing the HTTP binding as defined by the CCE specification.
 + * Currently a huge monolithic class. 
 + * @TODO refactor 
 + */
 +@SuppressWarnings("unchecked")
 +public class HTTPBindingProcessor extends AbstractBindingProcessor implements
 +    FormDataURLSupplier {
 +
 +  private static Log log = LogFactory.getLog(HTTPBindingProcessor.class);
 +
 +  private static enum State {
 +    INIT, PROCESS, DATAURL, TRANSFORM, FINISHED
 +  };
 +
 +  public final static Collection<String> XML_REQ_TRANSFER_ENCODING = Arrays
 +      .asList(new String[] { "binary" });
 +
 +  /**
 +   * Defines the maximum number of dataurl connects that are allowed within a
 +   * single SL Request processing.
 +   */
 +  protected static int MAX_DATAURL_HOPS = 10;
 +
 +  protected static String XML_MIME_TYPE = "text/xml";
 +  protected static String BINARY_MIME_TYPE = "application/octet-stream";
 +
 +  /**
 +   * If null everything is ok and the result is taken from the command invoker.
 +   */
 +  protected SLException bindingProcessorError;
 +  protected SLCommandInvoker commandInvoker;
 +  protected DataUrlResponse dataUrlResponse;
 +  protected Map<String, String> headerMap = Collections.EMPTY_MAP;
 +  protected SLCommand slCommand;
 +  protected Map<String, FormParameter> formParameterMap = new HashMap<String, FormParameter>();
 +  protected SLSourceContext srcContex = new SLSourceContext();
 +  protected SLTargetContext targetContext = new SLTargetContext();
 +  protected Protocol protocol;
 +  protected State currentState = State.INIT;
 +  protected Transformer transformer = null;
 +  protected String resultContentType = null;
 +  protected SLResult slResult = null;
 +  protected int responseCode = 200;
 +  protected Map<String, String> responseHeaders = Collections.EMPTY_MAP;
 +  protected Locale locale = Locale.getDefault();
 +
 +  /**
 +   * 
 +   * @param id
 +   *          may be null. In this case a new session id will be created.
 +   * @param cmdInvoker
 +   *          must not be null;
 +   */
 +  public HTTPBindingProcessor(String id, SLCommandInvoker cmdInvoker,
 +      Protocol protocol) {
 +    super(id);
 +    if ((protocol != Protocol.HTTP) && (protocol != Protocol.HTTPS)) {
 +      throw new SLRuntimeException("Protocol not supported: " + protocol);
 +    }
 +    if (cmdInvoker == null) {
 +      throw new NullPointerException("Commandinvoker cannot be set to null");
 +    }
 +    commandInvoker = cmdInvoker;
 +    this.protocol = protocol;
 +    srcContex.setSourceProtocol(protocol);
 +    srcContex.setSourceIsDataURL(false);
 +  }
 +
 +  //----------------------------------------------------------------------------
 +  // ----------- BEGIN CONVENIENCE METHODS -----------
 +
 +  protected void sendSTALQuit() {
 +    log.info("Sending QUIT command to STAL");
 +    List<STALRequest> quit = new ArrayList<STALRequest>(1);
 +    quit.add(new QuitRequest());
 +    getSTAL().handleRequest(quit);
 +  }
 +
 +  protected String getFormParameterAsString(String formParameterName) {
 +    FormParameter fp = formParameterMap.get(formParameterName);
 +    return getFormParameterAsString(fp);
 +  }
 +
 +  protected String getFormParameterAsString(FormParameter fp) {
 +    if (fp == null) {
 +      return null;
 +    }
 +    try {
 +      return StreamUtil.asString(fp.getFormParameterValue(), HttpUtil
 +          .getCharset(fp.getFormParameterContentType(), true));
 +    } catch (IOException e) {
 +      return null;
 +    }
 +  }
 +
 +  protected String getDataUrl() {
 +    return getFormParameterAsString(FixedFormParameters.DATAURL);
 +  }
 +
 +  protected String getStyleSheetUrl() {
 +    return getFormParameterAsString(FixedFormParameters.STYLESHEETURL);
 +  }
 +
 +  protected List<FormParameter> getFormParameters(String parameterNamePostfix) {
 +    List<FormParameter> resultList = new ArrayList<FormParameter>();
 +    for (Iterator<String> fpi = formParameterMap.keySet().iterator(); fpi
 +        .hasNext();) {
 +      String paramName = fpi.next();
 +      if (paramName.endsWith(parameterNamePostfix)) {
 +        resultList.add(formParameterMap.get(paramName));
 +      }
 +    }
 +    return resultList;
 +  }
 +
 +  protected List<FormParameter> getTransferHeaders() {
 +    return getFormParameters("__");
 +  }
 +
 +  protected List<FormParameter> getTransferForms() {
 +    List<FormParameter> resultList = new ArrayList<FormParameter>();
 +    for (Iterator<String> fpi = formParameterMap.keySet().iterator(); fpi
 +        .hasNext();) {
 +      String paramName = fpi.next();
 +      if ((paramName.endsWith("_")) && (!paramName.endsWith("__"))) {
 +        resultList.add(formParameterMap.get(paramName));
 +      }
 +    }
 +    return resultList;
 +  }
 +
 +  protected void closeDataUrlConnection() {
 +    log.debug("Closing data url input stream");
 +    if (dataUrlResponse == null) {
 +      return;
 +    }
 +    InputStream is = dataUrlResponse.getStream();
 +    if (is != null) {
 +      try {
 +        is.close();
 +      } catch (IOException e) {
 +        log.info("Error closing input stream to dataurl server:" + e);
 +      }
 +    }
 +  }
 +
 +  //----------------------------------------------------------------------------
 +  // ----------- END CONVENIENCE METHODS -----------
 +
 +  //----------------------------------------------------------------------------
 +  // -- BEGIN Methods that handle the http binding activities as defined in the
 +  // activity diagram --
 +
 +  protected void init() {
 +    log.info("Starting Bindingprocessor in Thread: "
 +        + Thread.currentThread().getId());
 +    if (bindingProcessorError != null) {
 +      log.debug("Detected binding processor error, sending quit command");
 +      // sendSTALQuit();
 +      currentState = State.FINISHED;
 +    } else if (slCommand == null) {
 +      log.error("SLCommand not set (consumeRequest not called ??)");
 +      bindingProcessorError = new SLException(2000);
 +      // sendSTALQuit();
 +      currentState = State.FINISHED;
 +    } else {
 +      currentState = State.PROCESS;
 +    }
 +  }
 +
 +  protected void processRequest() {
 +    log.debug("Entered State: " + State.PROCESS);
 +    log.debug("Processing command: " + slCommand);
 +    commandInvoker.setCommand(slCommand);
 +    responseCode = 200;
 +    responseHeaders = Collections.EMPTY_MAP;
 +    try {
 +      commandInvoker.invoke(srcContex);
 +    } catch (SLCanceledException e) {
 +      log.info("Caught exception: " + e);
 +      bindingProcessorError = e;
 +      currentState = State.TRANSFORM;
 +    }
 +    dataUrlResponse = null;
 +    if (getDataUrl() != null) {
 +      log.debug("Data Url set to: " + getDataUrl());
 +      currentState = State.DATAURL;
 +    } else {
 +      log.debug("No data url set");
 +      currentState = State.TRANSFORM;
 +    }
 +  }
 +
 +  protected void handleDataUrl() {
 +    log.debug("Entered State: " + State.DATAURL);
 +    try {
 +      DataUrl dataUrl = new DataUrl(getDataUrl());
 +      DataUrlConnection conn = dataUrl.openConnection();
 +
 +      // set transfer headers
 +      for (FormParameter fp : getTransferHeaders()) {
 +        String paramString = getFormParameterAsString(fp);
 +        if (paramString == null) {
 +          log.error("Got empty transfer header, ignoring this");
 +        } else {
 +          String[] keyVal = paramString.split(":", 2);
 +          String key = keyVal[0];
 +          String val = null;
 +          if (keyVal.length == 2) {
 +            val = keyVal[1];
 +          }
 +          val = val.trim();
 +          log.debug("Setting header " + key + " to value " + val);
 +          conn.setHTTPHeader(key, val);
 +        }
 +      }
 +
 +      // set transfer form parameters
 +      for (FormParameter fp : getTransferForms()) {
 +        String contentTransferEncoding = null;
 +        String contentType = fp.getFormParameterContentType();
 +        String charSet = HttpUtil.getCharset(contentType, false);
 +        if (charSet != null) {
 +          contentType = contentType.substring(0, contentType
 +              .lastIndexOf(HttpUtil.SEPERATOR[0]));
 +        }
 +        for (Iterator<String> header = fp.getHeaderNames(); header.hasNext();) {
 +          if (HttpUtil.CONTENT_TRANSFER_ENCODING
 +              .equalsIgnoreCase(header.next())) {
 +            contentTransferEncoding = getFormParameterAsString(fp);
 +          }
 +        }
 +        log.debug("Setting form: " + fp.getFormParameterName()
 +            + " contentType: " + contentType + " charset: " + charSet
 +            + " contentTransferEncoding: " + contentTransferEncoding);
 +        conn.setHTTPFormParameter(fp.getFormParameterName(), fp
 +            .getFormParameterValue(), contentType, charSet,
 +            contentTransferEncoding);
 +      }
 +
 +      // connect
 +      conn.connect();
 +      // fetch and set SL result
 +      targetContext.setTargetIsDataURL(true);
 +      targetContext.setTargetCertificate(conn.getServerCertificate());
 +      targetContext.setTargetProtocol(conn.getProtocol());
 +      SLResult result = commandInvoker.getResult(targetContext);
 +
 +      // transfer result
 +      conn.transmit(result);
 +
 +      // process Dataurl response
 +      dataUrlResponse = conn.getResponse();
 +      log.debug("Received data url response code: "
 +          + dataUrlResponse.getResponseCode());
 +      protocol = Protocol.fromString(conn.getProtocol());
 +
 +      switch (dataUrlResponse.getResponseCode()) {
 +      case 200:
 +        String contentType = dataUrlResponse.getContentType();
 +        log.debug("Got dataurl response content type: " + contentType);
 +        if (contentType != null) {
 +          if ((contentType.startsWith(HttpUtil.APPLICATION_URL_ENCODED))
 +              || (contentType.startsWith(HttpUtil.MULTIPART_FOTMDATA))) {
 +            log.debug("Detected SL Request in dataurl response");
 +            // process headers and request
 +            setHTTPHeaders(dataUrlResponse.getResponseHeaders());
 +            consumeRequestStream(dataUrlResponse.getStream());
 +            closeDataUrlConnection();
 +            srcContex.setSourceCertificate(conn.getServerCertificate());
 +            srcContex.setSourceIsDataURL(true);
 +            srcContex
 +                .setSourceProtocol(Protocol.fromString(conn.getProtocol()));
 +            currentState = State.PROCESS;
 +          } else if (((contentType.startsWith(HttpUtil.TXT_HTML))
 +              || (contentType.startsWith(HttpUtil.TXT_PLAIN)) || (contentType
 +              .startsWith(HttpUtil.TXT_XML)))
 +              && (dataUrlResponse.isHttpResponseXMLOK())) {
 +            log.info("Dataurl response matches <ok/> with content type: "
 +                + contentType);
 +            currentState = State.TRANSFORM;
 +
 +          } else if ((contentType.startsWith(HttpUtil.TXT_XML))
 +              && (!dataUrlResponse.isHttpResponseXMLOK())) {
 +            log
 +                .debug("Detected text/xml  dataurl response with content != <ok/>");
 +            headerMap.put(HttpUtil.HTTP_HEADER_CONTENT_TYPE, contentType);
 +            assignXMLRequest(dataUrlResponse.getStream(), HttpUtil.getCharset(
 +                contentType, true));
 +            closeDataUrlConnection();
 +            srcContex.setSourceCertificate(conn.getServerCertificate());
 +            srcContex.setSourceIsDataURL(true);
 +            srcContex
 +                .setSourceProtocol(Protocol.fromString(conn.getProtocol()));
 +            currentState = State.PROCESS;
 +            // just to be complete, actually not used
 +            srcContex.setSourceHTTPReferer(dataUrlResponse.getResponseHeaders()
 +                .get(HttpUtil.HTTP_HEADER_REFERER));
 +          } else {
 +            resultContentType = contentType;
 +            responseHeaders = dataUrlResponse.getResponseHeaders();
 +            responseCode = dataUrlResponse.getResponseCode();
 +            currentState = State.FINISHED;
 +          }
 +        } else {
 +          log.debug("Content type not set in dataurl response");
 +          closeDataUrlConnection();
 +          throw new SLBindingException(2007);
 +        }
 +
 +        break;
 +      case 307:
 +        contentType = dataUrlResponse.getContentType();
 +        if ((contentType != null) && (contentType.startsWith(HttpUtil.TXT_XML))) {
 +          log.debug("Received dataurl response code 307 with XML content");
 +          String location = dataUrlResponse.getResponseHeaders().get(
 +              HttpUtil.HTTP_HEADER_LOCATION);
 +          if (location == null) {
 +            log
 +                .error("Did not get a location header for a 307 data url response");
 +            throw new SLBindingException(2003);
 +          }
 +          // consumeRequestStream(dataUrlResponse.getStream());
 +          FormParameterStore fp = new FormParameterStore();
 +          fp.init(location.getBytes(HttpUtil.DEFAULT_CHARSET),
 +              FixedFormParameters.DATAURL, null, null);
 +          formParameterMap.put(FixedFormParameters.DATAURL, fp);
 +          headerMap.put(HttpUtil.HTTP_HEADER_CONTENT_TYPE, contentType);
 +          assignXMLRequest(dataUrlResponse.getStream(), HttpUtil.getCharset(
 +              dataUrlResponse.getContentType(), true));
 +          closeDataUrlConnection();
 +          srcContex.setSourceCertificate(conn.getServerCertificate());
 +          srcContex.setSourceIsDataURL(true);
 +          srcContex.setSourceProtocol(Protocol.fromString(conn.getProtocol()));
 +          currentState = State.PROCESS;
 +          // just to be complete, actually not used
 +          srcContex.setSourceHTTPReferer(dataUrlResponse.getResponseHeaders()
 +              .get(HttpUtil.HTTP_HEADER_REFERER));
 +
 +        } else {
 +          log.debug("Received dataurl response code 307 non XML content: "
 +              + dataUrlResponse.getContentType());
 +          resultContentType = dataUrlResponse.getContentType();
 +          currentState = State.FINISHED;
 +        }
 +        responseHeaders = dataUrlResponse.getResponseHeaders();
 +        responseCode = dataUrlResponse.getResponseCode();
 +        break;
 +
 +      case 301:
 +      case 302:
 +      case 303:
 +        responseHeaders = dataUrlResponse.getResponseHeaders();
 +        responseCode = dataUrlResponse.getResponseCode();
 +        resultContentType = dataUrlResponse.getContentType();
 +        currentState = State.FINISHED;
 +        break;
 +
 +      default:
 +        // issue error
 +        log.info("Unexpected response code from dataurl server: "
 +            + dataUrlResponse.getResponseCode());
 +        throw new SLBindingException(2007);
 +      }
 +
 +    } catch (SLException slx) {
 +      bindingProcessorError = slx;
 +      log.error("Error during dataurl communication");
 +      resultContentType = HttpUtil.TXT_XML;
 +      currentState = State.TRANSFORM;
 +    } catch (SSLHandshakeException hx) {
 +      bindingProcessorError = new SLException(2010);
 +      log.info("Error during dataurl communication", hx);
 +      resultContentType = HttpUtil.TXT_XML;
 +      currentState = State.TRANSFORM;
 +    } catch (IOException e) {
 +      bindingProcessorError = new SLBindingException(2001);
 +      log.error("Error while data url handling", e);
 +      resultContentType = HttpUtil.TXT_XML;
 +      currentState = State.TRANSFORM;
 +      return;
 +    }
 +  }
 +
 +  protected void transformResult() {
 +    log.debug("Entered State: " + State.TRANSFORM);
 +    if (bindingProcessorError != null) {
 +      resultContentType = HttpUtil.TXT_XML;
 +    } else if (dataUrlResponse != null) {
 +      resultContentType = dataUrlResponse.getContentType();
 +    } else {
 +      targetContext.setTargetIsDataURL(false);
 +      targetContext.setTargetProtocol(protocol.toString());
 +      try {
 +        slResult = commandInvoker.getResult(targetContext);
 +        resultContentType = slResult.getMimeType();
 +        log
 +            .debug("Successfully got SLResult from commandinvoker, setting mimetype to: "
 +                + resultContentType);
 +      } catch (SLCanceledException e) {
 +        log.info("Cannot get result from invoker:", e);
 +        bindingProcessorError = new SLException(6002);
 +        resultContentType = HttpUtil.TXT_XML;
 +      }
 +    }
 +    transformer = getTransformer(getStyleSheetUrl());
 +    if (transformer != null) {
 +      log.debug("Output transformation required");
 +      resultContentType = transformer.getOutputProperty("media-type");
 +      log.debug("Got media type from stylesheet: " + resultContentType);
 +      if (resultContentType == null) {
 +        log.debug("Setting to default text/xml result conent type");
 +        resultContentType = "text/xml";
 +      }
 +      log.debug("Deferring sytylesheet processing");
 +    }
 +    currentState = State.FINISHED;
 +  }
 +
 +  protected void finished() {
 +    log.debug("Entered State: " + State.FINISHED);
 +    if (bindingProcessorError != null) {
 +      log.debug("Binding processor error, sending quit command");
 +      resultContentType = HttpUtil.TXT_XML;
 +    }
 +    sendSTALQuit();
 +    log.info("Terminating Bindingprocessor; Thread: "
 +        + Thread.currentThread().getId());
 +  }
 +
 +  // -- END Methods that handle the http binding activities as defined in the
 +  // activity diagram --
 +  //----------------------------------------------------------------------------
 +
 +  /**
 +   * Sets the headers of the SL Request. IMPORTANT: make sure to set all headers
 +   * before invoking {@link #consumeRequestStream(InputStream)}
 +   * 
 +   * @param aHeaderMap
 +   *          if null all header will be cleared.
 +   */
 +  public void setHTTPHeaders(Map<String, String> aHeaderMap) {
 +    headerMap = new HashMap<String, String>();
 +    // ensure lowercase keys
 +    if (aHeaderMap != null) {
 +      for (String s : aHeaderMap.keySet()) {
 +        if (s != null) {
 +          headerMap.put(s.toLowerCase(), aHeaderMap.get(s));
 +          if (s.equalsIgnoreCase(HttpUtil.HTTP_HEADER_REFERER)) {
 +            String referer = aHeaderMap.get(s);
 +            log.debug("Got referer header: " + referer);
 +            srcContex.setSourceHTTPReferer(referer);
 +          }
 +        }
 +      }
 +    }
 +  }
 +
 +  public void setSourceCertificate(X509Certificate aCert) {
 +    srcContex.setSourceCertificate(aCert);
 +  }
 +
 +  /**
 +   * The HTTPBindingProcessor does not handle redirect URLs. It only provides
 +   * the parameter.
 +   * 
 +   * @return null if redirect url is not set.
 +   */
 +  public String getRedirectURL() {
 +    return getFormParameterAsString(FixedFormParameters.REDIRECTURL);
 +  }
 +
 +  public String getFormDataContentType(String aParameterName) {
 +    FormParameter fp = formParameterMap.get(aParameterName);
 +    if (fp != null) {
 +      return fp.getFormParameterContentType();
 +    }
 +    return null;
 +  }
 +
 +  public InputStream getFormData(String aParameterName) {
 +    FormParameter fp = formParameterMap.get(aParameterName);
 +    if (fp != null) {
 +      return fp.getFormParameterValue();
 +    }
 +    return null;
 +  }
 +
 +  protected void assignXMLRequest(InputStream is, String charset)
 +      throws IOException, SLException {
 +    Reader r = new InputStreamReader(is, charset);
 +    StreamSource source = new StreamSource(r);
 +    SLCommandContext commandCtx = new SLCommandContext();
 +    commandCtx.setSTAL(getSTAL());
 +    commandCtx.setURLDereferencerContext(new SimpleFormDataContextImpl(this));
 +    slCommand = SLCommandFactory.getInstance().createSLCommand(source,
 +        commandCtx);
 +    log.debug("Created new command: " + slCommand);
 +  }
 +
 +  @Override
 +  public void run() {
 +    boolean done = false;
 +    int hopcounter = 0;
 +    if (bindingProcessorError != null) {
 +      currentState = State.FINISHED;
 +    }
 +    try {
 +      while (!done) {
 +        try {
 +          switch (currentState) {
 +          case INIT:
 +            init();
 +            break;
 +          case PROCESS:
 +            processRequest();
 +            break;
 +          case DATAURL:
 +            handleDataUrl();
 +            if (++hopcounter > MAX_DATAURL_HOPS) {
 +              log.error("Maximum number of dataurl hops reached");
 +              bindingProcessorError = new SLBindingException(2000);
 +              currentState = State.FINISHED;
 +            }
 +            break;
 +          case TRANSFORM:
 +            transformResult();
 +            break;
 +          case FINISHED:
 +            done = true;
 +            finished();
 +            break;
 +          }
 +        } catch (RuntimeException rte) {
 +          throw rte;
 +        } catch (Exception t) {
 +          log.error("Caught unexpected exception", t);
 +          responseCode = 200;
 +          resultContentType = HttpUtil.TXT_XML;
 +          responseHeaders = Collections.EMPTY_MAP;
 +          bindingProcessorError = new SLException(2000);
 +          currentState = State.FINISHED;
 +        }
 +      }
 +    } catch (Throwable t) {
 +      log.error("Caught unexpected exception", t);
 +      responseCode = 200;
 +      resultContentType = HttpUtil.TXT_XML;
 +      responseHeaders = Collections.EMPTY_MAP;
 +      bindingProcessorError = new SLException(2000);
 +      currentState = State.FINISHED;
 +    }
 +    log.debug("Terminated http binding processor");
 +  }
 +
 +  @Override
 +  public void consumeRequestStream(InputStream is) {
 +    try {
 +      log.debug("Start consuming request stream");
 +      formParameterMap.clear();
 +      String cl = headerMap
 +          .get(HttpUtil.HTTP_HEADER_CONTENT_TYPE.toLowerCase());
 +      if (cl == null) {
 +        log.info("No content type set in http header");
 +        throw new SLBindingException(2006);
 +      }
 +      InputDecoder id = InputDecoderFactory.getDecoder(cl, is);
 +      id.setContentType(cl);
 +      if (id == null) {
 +        log.error("Cannot get inputdecoder for is");
 +        throw new SLException(2006);
 +      }
 +      for (Iterator<FormParameter> fpi = id.getFormParameterIterator(); fpi
 +          .hasNext();) {
 +        FormParameter fp = fpi.next();
 +        log.debug("Got request parameter with name: "
 +            + fp.getFormParameterName());
 +        if (fp.getFormParameterName().equals(FixedFormParameters.XMLREQUEST)) {
 +          log.debug("Creating XML Request");
 +          for (Iterator<String> headerIterator = fp.getHeaderNames(); headerIterator
 +              .hasNext();) {
 +            String headerName = headerIterator.next();
 +            if (HttpUtil.CONTENT_TRANSFER_ENCODING.equalsIgnoreCase(headerName)) {
 +              String transferEncoding = fp.getHeaderValue(headerName);
 +              log.debug("Got transfer encoding for xmlrequest: "
 +                  + transferEncoding);
 +              if (XML_REQ_TRANSFER_ENCODING.contains(transferEncoding)) {
 +                log.debug("Supported transfer encoding: " + transferEncoding);
 +              } else {
 +                log
 +                    .error("Transferencoding not supported: "
 +                        + transferEncoding);
 +                throw new SLBindingException(2005);
 +              }
 +            }
 +          }
 +          String charset = HttpUtil.getCharset(cl, true);
 +          assignXMLRequest(fp.getFormParameterValue(), charset);
 +        } else {
 +          FormParameterStore fps = new FormParameterStore();
 +          fps.init(fp);
 +          if (!fps.isEmpty()) {
 +            log.debug("Setting from parameter: " + fps.getFormParameterName());
 +            formParameterMap.put(fps.getFormParameterName(), fps);
 +          }
 +        }
 +      }
 +      if (slCommand == null) {
 +        throw new SLBindingException(2004);
 +      }
 +      if (is.read() != -1) {
 +        log.error("Request input stream not completely read");
 +        // consume rest of stream, should never occur
 +        throw new SLRuntimeException(
 +            "request input stream not consumed till end");
 +      }
 +    } catch (SLException slx) {
 +      log.info("Error while consuming input stream " + slx);
 +      bindingProcessorError = slx;
 +    } catch (Throwable t) {
 +      log.info("Error while consuming input stream " + t, t);
 +      bindingProcessorError = new SLException(2000);
 +    } finally {
 +      try {
 +        while (is.read() != -1)
 +          ;
 +      } catch (IOException e) {
 +        log.error(e);
 +      }
 +    }
 +  }
 +
 +  @Override
 +  public String getResultContentType() {
 +    return resultContentType;
 +  }
 +
 +  protected Transformer getTransformer(String styleSheetURL) {
 +    if (styleSheetURL == null) {
 +      log.debug("Stylesheet URL not set");
 +      return null;
 +    }
 +    try {
 +      URLDereferencerContext urlCtx = new SimpleFormDataContextImpl(this);
 +      URIResolver resolver = new URIResolverAdapter(URLDereferencer
 +          .getInstance(), urlCtx);
 +      TransformerFactory factory = TransformerFactory.newInstance();
 +      StreamData sd = URLDereferencer.getInstance().dereference(styleSheetURL,
 +          urlCtx);
 +      Transformer t = factory.newTransformer(new StreamSource(sd.getStream()));
 +      t.setURIResolver(resolver);
 +      return t;
 +    } catch (Exception ex) {
 +      log.info("Cannot instantiate transformer", ex);
 +      bindingProcessorError = new SLException(2002);
 +      return null;
 +    }
 +  }
 +
 +  protected void handleBindingProcessorError(OutputStream os, String encoding,
 +      Transformer transformer) throws IOException {
 +    log.debug("Writing error as result");
 +    ErrorResultImpl error = new ErrorResultImpl(bindingProcessorError);
 +    try {
 +      error.writeTo(new StreamResult(new OutputStreamWriter(os, encoding)),
 +          transformer);
 +    } catch (TransformerException e) {
 +      log.fatal("Cannot write error result to stream", e);
 +    }
 +  }
 +
 +  @Override
 +  public void writeResultTo(OutputStream os, String encoding)
 +      throws IOException {
 +    if (encoding == null) {
 +      encoding = HttpUtil.DEFAULT_CHARSET;
 +    }
 +    if (bindingProcessorError != null) {
 +      log.debug("Detected error in binding processor, writing error as result");
 +      handleBindingProcessorError(os, encoding, transformer);
 +      return;
 +    } else if (dataUrlResponse != null) {
 +      log.debug("Writing data url response  as result");
 +      String charEnc = HttpUtil.getCharset(dataUrlResponse.getContentType(),
 +          true);
 +      InputStreamReader isr = new InputStreamReader(
 +          dataUrlResponse.getStream(), charEnc);
 +      OutputStreamWriter osw = new OutputStreamWriter(os, encoding);
 +      if (transformer == null) {
 +        StreamUtil.copyStream(isr, osw);
 +      } else {
 +        try {
 +          transformer.transform(new StreamSource(isr), new StreamResult(osw));
 +        } catch (TransformerException e) {
 +          log.fatal("Exception occured during result transformation", e);
 +          // bindingProcessorError = new SLException(2008);
 +          // handleBindingProcessorError(os, encoding, null);
 +          return;
 +        }
 +      }
 +      osw.flush();
 +      isr.close();
 +    } else if (slResult == null) {
 +      // result not yet assigned -> must be a cancel
 +      bindingProcessorError = new SLException(6001);
 +      handleBindingProcessorError(os, encoding, transformer);
 +      return;
 +    } else {
 +      log.debug("Getting result from invoker");
 +      OutputStreamWriter osw = new OutputStreamWriter(os, encoding);
 +      try {
 +        slResult.writeTo(new StreamResult(osw), transformer);
 +      } catch (TransformerException e) {
 +        log.fatal("Cannot write result to stream", e);
 +        // bindingProcessorError = new SLException(2008);
 +        // handleBindingProcessorError(os, encoding, transformer);
 +      }
 +      osw.flush();
 +    }
 +  }
 +
 +  /**
 +   * The response code from the dataurl server or 200 if no dataurl server
 +   * created the result
 +   * 
 +   * @return
 +   */
 +  public int getResponseCode() {
 +    return responseCode;
 +  }
 +
 +  /**
 +   * All headers from the data url server in case of a direct forward from the
 +   * dataurl server.
 +   * 
 +   * @return
 +   */
 +  public Map<String, String> getResponseHeaders() {
 +    return responseHeaders;
 +  }
 +
 +  @Override
 +  public void setLocale(Locale locale) {
 +    if (locale == null) {
 +      throw new NullPointerException("Locale must not be set to null");
 +    }
 +    this.locale = locale;
 +  }
 +
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/HttpUtil.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/HttpUtil.java new file mode 100644 index 00000000..b11a4d85 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/HttpUtil.java @@ -0,0 +1,78 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +import java.util.Map;
 +
 +import org.apache.commons.fileupload.ParameterParser;
 +
 +/**
 + * Placeholder for some HTTP related constants and helper method to extract the charset for a request. 
 + *
 + */
 +public class HttpUtil {
 +
 +  public final static String CHAR_SET = "charset";
 +  public final static String DEFAULT_CHARSET = "ISO-8859-1";
 +  public final static String HTTP_HEADER_CONTENT_TYPE = "Content-Type";
 +  public static final String HTTP_HEADER_USER_AGENT = "User-Agent";
 +  public final static String HTTP_HEADER_REFERER = "Referer";
 +  public final static String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding";
 +  public final static String MULTIPART_FOTMDATA = "multipart/form-data";
 +  public final static String MULTIPART_FOTMDATA_BOUNDARY = "boundary";
 +  public final static String TXT_XML = "text/xml";
 +  public final static String TXT_PLAIN = "text/plain";
 +  public final static String TXT_HTML = "text/html";
 +  public final static String APPLICATION_URL_ENCODED = "application/x-www-form-urlencoded";
 +  public final static String HTTP_HEADER_LOCATION = "Location";
 +
 +  public final static char[] SEPERATOR = { ';' };
 +
 +  /**
 +   * Extracts charset from a content type header.
 +   * 
 +   * @param contentType
 +   * @param replaceNullWithDefault
 +   *          if true the method return the default charset if not set
 +   * @return charset String or null if not present
 +   */
 +  @SuppressWarnings("unchecked")
 +  public static String getCharset(String contentType,
 +      boolean replaceNullWithDefault) {
 +    ParameterParser pf = new ParameterParser();
 +    pf.setLowerCaseNames(true);
 +    Map map = pf.parse(contentType, SEPERATOR);
 +    String retVal = (String) map.get(CHAR_SET);
 +    if ((retVal == null) && (replaceNullWithDefault)) {
 +      if (map.containsKey(APPLICATION_URL_ENCODED)) {
 +        // default charset for url encoded data
 +        return "UTF-8";
 +      }
 +      retVal = getDefaultCharset();
 +    }
 +    return retVal;
 +  }
 +
 +  /**
 +   * 
 +   * Not to be used for url encoded requests.
 +   */
 +  public static String getDefaultCharset() {
 +    return DEFAULT_CHARSET;
 +  }
 +
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/Id.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/Id.java new file mode 100644 index 00000000..93ab2e8b --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/Id.java @@ -0,0 +1,27 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +/**
 + * The unique identifier for a BindingProcessor
 + * @author wbauer
 + *
 + */
 +public interface Id {
 +
 +  public String toString();
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/IdFactory.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/IdFactory.java new file mode 100644 index 00000000..60bf69a4 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/IdFactory.java @@ -0,0 +1,106 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +import java.security.NoSuchAlgorithmException;
 +import java.security.SecureRandom;
 +
 +import org.apache.commons.logging.Log;
 +import org.apache.commons.logging.LogFactory;
 +
 +/**
 + * Creates or converts Ids for BindingProcessors.
 + * @author wbauer
 + *
 + */
 +public class IdFactory {
 +
 +  public static int DEFAULT_NUMBER_OF_BITS = 168;
 +
 +  private static Log log = LogFactory.getLog(IdFactory.class);
 +
 +  private static IdFactory instance = new IdFactory();
 +
 +  private SecureRandom random;
 +  private int numberOfBits = DEFAULT_NUMBER_OF_BITS;
 +
 +  private IdFactory() {
 +    try {
 +      random = SecureRandom.getInstance("SHA1PRNG");
 +    } catch (NoSuchAlgorithmException e) {
 +      log.error("Cannot instantiate secure random" + e);
 +    }
 +  }
 +
 +  public static IdFactory getInstance() {
 +    return instance;
 +  }
 +
 +
 +  /**
 +   * set the secure random number generator to create secure ids.
 +   * 
 +   * @param random
 +   *          must not be null
 +   */
 +  public void setSecureRandom(SecureRandom random) {
 +    if (random == null) {
 +      throw new NullPointerException("Cannot set secure random to null");
 +    }
 +    this.random = random;
 +  }
 +
 +  /**
 +   * Don't use this method unless you know exactly what you do !
 +   * Be sure to use a sufficient large entropy 
 +   * @param numberOfBits >=1 (although this small entropy does not make sense)
 +   */
 +  public void setNumberOfBits(int numberOfBits) {
 +    if (numberOfBits <1) {
 +      throw new IllegalArgumentException("Cannot set number of bits < 1");
 +    }
 +    this.numberOfBits = numberOfBits;
 +  }
 +
 +  public int getNumberOfBits() {
 +    return numberOfBits;
 +  }
 +  
 +  /**
 +   * Creates a new Id object with the factory's secure RNG and the set number of
 +   * bits.
 +   * 
 +   * @return
 +   */
 +  public Id createId() {
 +    return new IdImpl(numberOfBits, random);
 +  }
 +
 +  /**
 +   * Creates an Id object for the provided String
 +   * 
 +   * @param idString
 +   *          may be null in this case the method call creates a new Id.
 +   * @return
 +   */
 +  public Id createId(String idString) {
 +    if (idString == null) {
 +      return createId();
 +    }
 +    return new IdImpl(idString);
 +  }
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/IdImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/IdImpl.java new file mode 100644 index 00000000..5523992a --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/IdImpl.java @@ -0,0 +1,80 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +import iaik.utils.Base64OutputStream;
 +
 +import java.io.ByteArrayOutputStream;
 +import java.io.IOException;
 +import java.security.SecureRandom;
 +
 +import org.apache.commons.logging.Log;
 +import org.apache.commons.logging.LogFactory;
 +
 +/**
 + * Implementation that uses a Base64 representation for self generated Ids.
 + * @author wbauer
 + *
 + */
 +public class IdImpl implements at.gv.egiz.bku.binding.Id {
 +  private static Log log = LogFactory.getLog(IdImpl.class);
 +  
 +  private String idString;
 +
 +  public IdImpl(int bitNumber, SecureRandom random) {
 +    int byteSize = bitNumber/8;
 +    if (bitNumber % 8 != 0) {
 +      byteSize++;
 +    }
 +    byte[] randomBytes = new byte[byteSize];
 +    random.nextBytes(randomBytes);
 +    ByteArrayOutputStream baos = new ByteArrayOutputStream();
 +    Base64OutputStream b64 = new Base64OutputStream(baos);
 +    try {
 +      b64.write(randomBytes);
 +      b64.flush();
 +      b64.close();
 +      idString = new String(baos.toByteArray());
 +    } catch (IOException e) {
 +      log.error("Cannot create secure id: "+e);
 +    }
 +  }
 +
 +  public IdImpl(String idString) {
 +    if (idString == null) {
 +      throw new NullPointerException("Provided idstring must not be null");
 +    }
 +    this.idString = idString;
 +  }
 +
 +  public String toString() {
 +    return idString;
 +  }
 +  
 +  public int hashCode() {
 +    return idString.hashCode();
 +  }
 +  
 +  public boolean equals(Object other) {
 +    if (other instanceof Id) {
 +      Id otherId = (Id)other;
 +      return otherId.toString().equals(idString);
 +    } else {
 +      return false;
 +    }
 +  }
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/InputDecoder.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/InputDecoder.java new file mode 100644 index 00000000..e22e54f2 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/InputDecoder.java @@ -0,0 +1,41 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +import java.io.InputStream;
 +import java.util.Iterator;
 +
 +/**
 + * Decodes http input stream (either url encoded or multipart formdata)
 + * @author wbauer
 + *
 + */
 +public interface InputDecoder {
 +  /**
 +   * Called from Factory.
 +   * @param contentType
 +   */
 +  void setContentType(String contentType);
 +  
 +  /**
 +   * Called from Factory.
 +   * @param is the input must not be null
 +   */
 +  void setInputStream(InputStream is);
 +  
 +  Iterator<FormParameter> getFormParameterIterator();
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/InputDecoderFactory.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/InputDecoderFactory.java new file mode 100644 index 00000000..211deee7 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/InputDecoderFactory.java @@ -0,0 +1,89 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +import java.io.InputStream;
 +import java.util.HashMap;
 +import java.util.Map;
 +
 +import org.apache.commons.logging.Log;
 +import org.apache.commons.logging.LogFactory;
 +
 +/**
 + * Factory to get a matching instance for a encoded input stream when reading a http request.
 + *
 + */
 +public class InputDecoderFactory {
 +
 +  public final static String MULTIPART_FORMDATA = "multipart/form-data";
 +  public final static String URL_ENCODED = "application/x-www-form-urlencoded";
 +
 +  private static InputDecoderFactory instance = new InputDecoderFactory();
 +  private static Log log = LogFactory.getLog(InputDecoderFactory.class);
 +
 +  private String defaultEncoding = URL_ENCODED;
 +  private Map<String, Class<? extends InputDecoder>> decoderMap = new HashMap<String, Class<? extends InputDecoder>>();
 +
 +  private InputDecoderFactory() {
 +    decoderMap.put(MULTIPART_FORMDATA, MultiPartFormDataInputDecoder.class);
 +    decoderMap.put(URL_ENCODED, XWWWFormUrlInputDecoder.class);
 +  }
 +
 +  public static InputDecoder getDefaultDecoder(InputStream is) {
 +    return getDecoder(instance.defaultEncoding, is);
 +  }
 +
 +  /**
 +   * 
 +   * @param contentType
 +   * @param is
 +   * @return null if the content type is not supported
 +   */
 +  public static InputDecoder getDecoder(String contentType, InputStream is) {
 +    String prefix = contentType.split(";")[0].trim().toLowerCase();
 +    Class<? extends InputDecoder> dec = instance.decoderMap.get(prefix);
 +    if (dec == null) {
 +      log.info("Unknown encoding prefix " + contentType);
 +      return null;
 +    }
 +    InputDecoder id;
 +    try {
 +      id = dec.newInstance();
 +      id.setContentType(contentType);
 +      id.setInputStream(is);
 +      return id;
 +    } catch (InstantiationException e) {
 +      log.error(e);
 +      throw new IllegalArgumentException(
 +          "Cannot get an input decoder for content type: " + contentType);
 +    } catch (IllegalAccessException e) {
 +      log.error(e);
 +      throw new IllegalArgumentException(
 +          "Cannot get an input decoder for content type: " + contentType);
 +    }
 +  }
 +
 +  /**
 +   * Allows to register decoders for special mime types.
 +   * @param mimeType
 +   * @param decoder
 +   */
 +  public static void registerDecoder(String mimeType,
 +      Class<? extends InputDecoder> decoder) {
 +    instance.decoderMap.put(mimeType.toLowerCase(), decoder);
 +  }
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/MultiPartFormDataInputDecoder.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/MultiPartFormDataInputDecoder.java new file mode 100644 index 00000000..f8b13553 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/MultiPartFormDataInputDecoder.java @@ -0,0 +1,133 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.util.Iterator;
 +
 +import org.apache.commons.fileupload.FileItemIterator;
 +import org.apache.commons.fileupload.FileItemStream;
 +import org.apache.commons.fileupload.FileUpload;
 +import org.apache.commons.fileupload.FileUploadException;
 +import org.apache.commons.fileupload.RequestContext;
 +import org.apache.commons.logging.Log;
 +import org.apache.commons.logging.LogFactory;
 +
 +import at.gv.egiz.bku.slexceptions.SLRuntimeException;
 +
 +/**
 + * The code to detect the multipart boundary is based on
 + * org.apache.commons.fileupload.FileUploadBase of
 + * http://commons.apache.org/fileupload/
 + * 
 + * @author wbauer
 + * 
 + */
 +public class MultiPartFormDataInputDecoder implements InputDecoder,
 +    RequestContext {
 +
 +  private static Log log = LogFactory
 +      .getLog(MultiPartFormDataInputDecoder.class);
 +
 +  private String contentType;
 +  private InputStream stream;
 +
 +  @Override
 +  public void setContentType(String contentType) {
 +    this.contentType = contentType;
 +  }
 +
 +  @Override
 +  public String getCharacterEncoding() {
 +    return null;
 +  }
 +
 +  @Override
 +  public int getContentLength() {
 +    return 0;
 +  }
 +
 +  @Override
 +  public String getContentType() {
 +    return contentType;
 +  }
 +
 +  @Override
 +  public InputStream getInputStream() throws IOException {
 +    return stream;
 +  }
 +
 +  @Override
 +  public Iterator<FormParameter> getFormParameterIterator() {
 +    try {
 +      FileUpload fup = new FileUpload();
 +      FileItemIterator fit = fup.getItemIterator(this);
 +      return new IteratorDelegator(fit);
 +    } catch (Exception iox) {
 +      log.error("Cannot decode multipart form data stream " + iox);
 +      throw new SLRuntimeException(iox);
 +    }
 +  }
 +
 +  @Override
 +  public void setInputStream(InputStream is) {
 +    stream = is;
 +  }
 +
 +  static class IteratorDelegator implements Iterator<FormParameter> {
 +
 +    private FileItemIterator fileItemIterator;
 +
 +    public IteratorDelegator(FileItemIterator fit) {
 +      fileItemIterator = fit;
 +    }
 +
 +    @Override
 +    public boolean hasNext() {
 +      try {
 +        return fileItemIterator.hasNext();
 +      } catch (FileUploadException e) {
 +        log.error(e);
 +        throw new SLRuntimeException(e);
 +      } catch (IOException e) {
 +        log.error(e);
 +        throw new SLRuntimeException(e);
 +      }
 +    }
 +
 +    @Override
 +    public FormParameter next() {
 +      try {
 +        FileItemStream item = fileItemIterator.next();
 +        return new FormParameterImpl(item.getContentType(),
 +            item.getFieldName(), item.openStream(), item.getHeaders());
 +      } catch (FileUploadException e) {
 +        log.error(e);
 +        throw new SLRuntimeException(e);
 +      } catch (IOException e) {
 +        log.error(e);
 +        throw new SLRuntimeException(e);
 +      }
 +    }
 +
 +    @Override
 +    public void remove() {
 +      throw new UnsupportedOperationException("Remove not supported");
 +    }
 +  }
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/RemovalStrategy.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/RemovalStrategy.java new file mode 100644 index 00000000..6c2dcb9f --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/RemovalStrategy.java @@ -0,0 +1,26 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +/**
 + * Could be used to remove expired BindingProcessor objects from a BindingProcessorManager. 
 + * 
 + */
 +public interface RemovalStrategy {
 +  public void execute();
 +  public void setBindingProcessorManager(BindingProcessorManager bp);
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/SLCommandInvokerImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/SLCommandInvokerImpl.java new file mode 100644 index 00000000..ef2affd1 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/SLCommandInvokerImpl.java @@ -0,0 +1,66 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +import org.apache.commons.logging.Log;
 +import org.apache.commons.logging.LogFactory;
 +
 +import at.gv.egiz.bku.slcommands.SLCommand;
 +import at.gv.egiz.bku.slcommands.SLCommandInvoker;
 +import at.gv.egiz.bku.slcommands.SLResult;
 +import at.gv.egiz.bku.slcommands.SLSourceContext;
 +import at.gv.egiz.bku.slcommands.SLTargetContext;
 +
 +/**
 + * This class implements the entry point for the CCEs security management.
 + * 
 + * TODO the secuirty management is currently not implemented.
 + */
 +public class SLCommandInvokerImpl implements SLCommandInvoker {
 +  
 +  private static Log log = LogFactory.getLog(SLCommandInvokerImpl.class);
 +
 +  protected SLCommand command;
 +  protected SLResult result;
 +
 +  /**
 +   * Invokes a sl command. 
 +   */
 +  public void invoke(SLSourceContext aContext) {
 +    // FIXXME add security policy here.
 +    log.warn("Security policy not implemented yet, invoking command: "+command);
 +    result = command.execute();
 +  }
 +
 +  public SLResult getResult(SLTargetContext aContext) {
 +    // FIXXME
 +    log.warn("Security policy not implemented yet, getting result of command: "+command);
 +    return result;
 +  }
 +
 +  public void setCommand(SLCommand aCmd) {
 +    command = aCmd;
 +  }
 +
 +  @Override
 +  public SLCommandInvoker newInstance() {
 +    SLCommandInvokerImpl cmdInv = new SLCommandInvokerImpl();
 +    return cmdInv;
 +  }
 +  
 +  
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/XWWWFormUrlInputDecoder.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/XWWWFormUrlInputDecoder.java new file mode 100644 index 00000000..f4ebe288 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/XWWWFormUrlInputDecoder.java @@ -0,0 +1,101 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.binding;
 +
 +import java.io.ByteArrayInputStream;
 +import java.io.ByteArrayOutputStream;
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.net.URLDecoder;
 +import java.util.Iterator;
 +import java.util.LinkedList;
 +import java.util.List;
 +import java.util.Map;
 +
 +import org.apache.commons.fileupload.ParameterParser;
 +
 +import at.gv.egiz.bku.slexceptions.SLRuntimeException;
 +import at.gv.egiz.bku.utils.StreamUtil;
 +
 +/**
 + * Implementation based on Java's URLDecoder class 
 + *
 + */
 +// FIXME replace this code by a streaming variant
 +public class XWWWFormUrlInputDecoder implements InputDecoder {
 +
 +  public final static String CHAR_SET = "charset";
 +  public final static String NAME_VAL_SEP = "=";
 +  public final static String SEP = "\\&";
 +
 +  private String contentType;
 +  private InputStream dataStream;
 +  private String charset = "UTF-8";
 +
 +  protected List<FormParameter> decodeInput(InputStream is) throws IOException {
 +    List<FormParameter> result = new LinkedList<FormParameter>();
 +    ByteArrayOutputStream bos = new ByteArrayOutputStream();
 +    StreamUtil.copyStream(is, bos);
 +    String inputString = new String(bos.toByteArray());
 +    String[] nameValuePairs = inputString.split(SEP);
 +    //inputString = URLDecoder.decode(inputString, charset);
 +    for (int i = 0; i < nameValuePairs.length; i++) {
 +      String[] fields = nameValuePairs[i].split(NAME_VAL_SEP, 2);
 +      if (fields.length != 2) {
 +        throw new SLRuntimeException("Invalid form encoding, missing value");
 +      }
 +      String name = URLDecoder.decode(fields[0], charset); 
 +      String value =URLDecoder.decode(fields[1], charset);
 +      ByteArrayInputStream bais = new ByteArrayInputStream(value
 +          .getBytes(charset));
 +      FormParameterImpl fpi = new FormParameterImpl(contentType, name, bais, null);
 +      result.add(fpi);
 +    }
 +    return result;
 +  }
 +
 +  @SuppressWarnings("unchecked")
 +  @Override
 +  public void setContentType(String contentType) {
 +    ParameterParser pp = new ParameterParser();
 +    pp.setLowerCaseNames(true);
 +    Map<String, String> params = pp.parse(contentType, new char[] { ':', ';' });
 +    if (!params.containsKey("application/x-www-form-urlencoded")) {
 +      throw new IllegalArgumentException(
 +          "not a url encoded content type specification: " + contentType);
 +    }
 +    String cs = params.get(CHAR_SET);
 +    if (cs != null) {
 +      charset = cs;
 +    }
 +    this.contentType = contentType;
 +  }
 +
 +  @Override
 +  public Iterator<FormParameter> getFormParameterIterator() {
 +    try {
 +      return decodeInput(dataStream).iterator();
 +    } catch (IOException e) {
 +      throw new SLRuntimeException(e);
 +    }
 +  }
 +
 +  @Override
 +  public void setInputStream(InputStream is) {
 +    dataStream = is;
 +  }
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/multipart/InputStreamPartSource.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/multipart/InputStreamPartSource.java new file mode 100644 index 00000000..253f8ff5 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/multipart/InputStreamPartSource.java @@ -0,0 +1,66 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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. +*/ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package at.gv.egiz.bku.binding.multipart; + +import java.io.IOException; +import java.io.InputStream; +import org.apache.commons.httpclient.methods.multipart.PartSource; + +/** + * InputStream source for FilePart.  + * DOES NOT RETURN A CORRECT LENGTH OF THE INPUT DATA. (but we don't care, since we use chunked encoding) + *  + * @author clemens + */ +public class InputStreamPartSource implements PartSource { + +    protected String name; +    protected InputStream data; +     +    public InputStreamPartSource(String name, InputStream data) { +        this.name = name; +        this.data = data; +    } +     +    /** +     * Just a dummy value to make Part work +     * @return 42 +     */ +    @Override +    public long getLength() { +        //System.out.println("***********GETLENGTH"); +        return 42; +    } + +    @Override +    public String getFileName() { +        return name; +    } + +    @Override +    public InputStream createInputStream() throws IOException { +        if (data == null)  +            throw new IOException("Failed to get stream for part: no data was set."); +        return data; +    } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/multipart/SLResultPart.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/multipart/SLResultPart.java new file mode 100644 index 00000000..566b77b3 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/multipart/SLResultPart.java @@ -0,0 +1,57 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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. +*/ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package at.gv.egiz.bku.binding.multipart; + +import at.gv.egiz.bku.slcommands.SLResult; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; + +import javax.xml.transform.stream.StreamResult; + +import org.apache.commons.httpclient.methods.multipart.ByteArrayPartSource; +import org.apache.commons.httpclient.methods.multipart.FilePart; + +/** + *  + * @author clemens + */ +public class SLResultPart extends FilePart { + +  protected SLResult slResult; +  protected String encoding; + +  public SLResultPart(SLResult slResult, String encoding) { +    super("XMLResponse", +        new ByteArrayPartSource(null, "dummySource".getBytes())); +    this.slResult = slResult; +    this.encoding = encoding; +  } + +  @Override +  protected void sendData(OutputStream out) throws IOException { +    slResult.writeTo(new StreamResult(new OutputStreamWriter(out, encoding))); +    // slResult.writeTo(new StreamResult(new OutputStreamWriter(System.out, +    // encoding))); +    // super.sendData(out); +  } +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/AccessControlInvocation.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/AccessControlInvocation.java new file mode 100644 index 00000000..014b7fd7 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/AccessControlInvocation.java @@ -0,0 +1,21 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands;
 +
 +public class AccessControlInvocation implements
 +    at.gv.egiz.bku.slcommands.InvocationStrategy {
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/CreateXMLSignatureCommand.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/CreateXMLSignatureCommand.java new file mode 100644 index 00000000..2d87c39f --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/CreateXMLSignatureCommand.java @@ -0,0 +1,25 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands;
 +
 +import at.gv.egiz.bku.slexceptions.SLCommandException;
 +import at.gv.egiz.bku.slexceptions.SLRequestException;
 +
 +public interface CreateXMLSignatureCommand extends SLCommand {
 +
 +  public void prepareXMLSignature() throws SLCommandException, SLRequestException;
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/CreateXMLSignatureResult.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/CreateXMLSignatureResult.java new file mode 100644 index 00000000..4bc2820b --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/CreateXMLSignatureResult.java @@ -0,0 +1,20 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands;
 +
 +public interface CreateXMLSignatureResult extends SLResult {
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/ErrorResult.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/ErrorResult.java new file mode 100644 index 00000000..5d52c0ea --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/ErrorResult.java @@ -0,0 +1,20 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands;
 +
 +public interface ErrorResult extends SLResult {
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/InfoboxReadCommand.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/InfoboxReadCommand.java new file mode 100644 index 00000000..77529a36 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/InfoboxReadCommand.java @@ -0,0 +1,20 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands;
 +
 +public interface InfoboxReadCommand extends SLCommand {
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/InfoboxReadResult.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/InfoboxReadResult.java new file mode 100644 index 00000000..c6a51362 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/InfoboxReadResult.java @@ -0,0 +1,20 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands;
 +
 +public interface InfoboxReadResult extends SLResult {
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/InvocationStrategy.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/InvocationStrategy.java new file mode 100644 index 00000000..6b410fac --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/InvocationStrategy.java @@ -0,0 +1,20 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands;
 +
 +public interface InvocationStrategy {
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/NullOperationCommand.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/NullOperationCommand.java new file mode 100644 index 00000000..0651f882 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/NullOperationCommand.java @@ -0,0 +1,20 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands;
 +
 +public interface NullOperationCommand extends SLCommand {
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/NullOperationResult.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/NullOperationResult.java new file mode 100644 index 00000000..c36c879e --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/NullOperationResult.java @@ -0,0 +1,20 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands;
 +
 +public interface NullOperationResult extends SLResult {
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommand.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommand.java new file mode 100644 index 00000000..a8625946 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommand.java @@ -0,0 +1,31 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands;
 +
 +import at.gv.egiz.bku.slexceptions.SLCommandException;
 +
 +public interface SLCommand {
 +  
 +  public final String NAMESPACE_URI = "http://www.buergerkarte.at/namespaces/securitylayer/1.2#";
 +  
 +  public String getName();
 +
 +  public void init(SLCommandContext aCtx, Object aUnmarshalledRequest) throws SLCommandException;
 +
 +  public SLResult execute();
 +
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandContext.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandContext.java new file mode 100644 index 00000000..c95736bd --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandContext.java @@ -0,0 +1,42 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands;
 +
 +import at.gv.egiz.bku.utils.urldereferencer.URLDereferencerContext;
 +import at.gv.egiz.stal.STAL;
 +
 +public class SLCommandContext {
 +  
 +  private STAL stal;
 +  private URLDereferencerContext urlDerefCtx;
 +
 +  public void setSTAL(STAL aStal) {
 +    this.stal = aStal;
 +  }
 +
 +  public void setURLDereferencerContext(URLDereferencerContext aCtx) {
 +    this.urlDerefCtx = aCtx;
 +  }
 +
 +  public STAL getSTAL() {
 +    return stal;
 +  }
 +
 +  public URLDereferencerContext getURLDereferencerContext() {
 +    return urlDerefCtx;
 +  }
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandFactory.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandFactory.java new file mode 100644 index 00000000..e13b29a1 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandFactory.java @@ -0,0 +1,370 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands;
 +
 +import java.io.IOException;
 +import java.net.URL;
 +import java.util.HashMap;
 +import java.util.Map;
 +
 +import javax.xml.XMLConstants;
 +import javax.xml.bind.JAXBContext;
 +import javax.xml.bind.JAXBElement;
 +import javax.xml.bind.JAXBException;
 +import javax.xml.bind.UnmarshalException;
 +import javax.xml.bind.Unmarshaller;
 +import javax.xml.namespace.QName;
 +import javax.xml.stream.XMLEventReader;
 +import javax.xml.stream.XMLInputFactory;
 +import javax.xml.stream.XMLStreamException;
 +import javax.xml.transform.Source;
 +import javax.xml.transform.stream.StreamSource;
 +import javax.xml.validation.Schema;
 +import javax.xml.validation.SchemaFactory;
 +
 +import org.apache.commons.logging.Log;
 +import org.apache.commons.logging.LogFactory;
 +import org.xml.sax.SAXException;
 +import org.xml.sax.SAXParseException;
 +
 +import at.gv.egiz.bku.slcommands.impl.CreateXMLSignatureCommandImpl;
 +import at.gv.egiz.bku.slcommands.impl.InfoboxReadCommandImpl;
 +import at.gv.egiz.bku.slcommands.impl.NullOperationCommandImpl;
 +import at.gv.egiz.bku.slexceptions.SLCommandException;
 +import at.gv.egiz.bku.slexceptions.SLExceptionMessages;
 +import at.gv.egiz.bku.slexceptions.SLRequestException;
 +import at.gv.egiz.bku.slexceptions.SLRuntimeException;
 +import at.gv.egiz.slbinding.RedirectEventFilter;
 +import at.gv.egiz.slbinding.RedirectUnmarshallerListener;
 +
 +public class SLCommandFactory {
 +
 +    /**
 +     * Schema files required for Security Layer command validation.
 +     */
 +    public static final String[] SCHEMA_FILES = new String[]{
 +        "at/gv/egiz/bku/slcommands/schema/xml.xsd",
 +        "at/gv/egiz/bku/slcommands/schema/xmldsig-core-schema.xsd",
 +        "at/gv/egiz/bku/slcommands/schema/Core-1.2.xsd"
 +    };
 +    /**
 +     * Logging facility.
 +     */
 +    static Log log = LogFactory.getLog(SLCommandFactory.class);
 +    /**
 +     * The instance returned by {@link #getInstance()}.
 +     */
 +    private static SLCommandFactory instance;
 +    /**
 +     * Schema for Security Layer command validation.
 +     */
 +    private static Schema slSchema;
 +    /**
 +     * The JAXBContext.
 +     */
 +    private static JAXBContext jaxbContext;
 +    /**
 +     * The map of <namespaceURI>:<localName> to implementation class of the
 +     * corresponding {@link SLCommand}.
 +     */
 +    private static Map<String, Class<? extends SLCommand>> slRequestTypeMap = new HashMap<String, Class<? extends SLCommand>>();
 +    
 +
 +    static {
 +
 +        // TODO: implement dynamic registration
 +
 +        // register all known implementation classes
 +        putImplClass(SLCommand.NAMESPACE_URI, "NullOperationRequest",
 +          NullOperationCommandImpl.class);
 +        putImplClass(SLCommand.NAMESPACE_URI, "InfoboxReadRequest",
 +          InfoboxReadCommandImpl.class);
 +        putImplClass(SLCommand.NAMESPACE_URI, "CreateXMLSignatureRequest",
 +          CreateXMLSignatureCommandImpl.class);
 +    }
 +
 +    /**
 +     * Register an {@link SLCommand} implementation class of a Security Layer
 +     * command with the given <code>namespaceUri</code> and <code>localname</code>
 +     * .
 +     * 
 +     * @param namespaceUri
 +     *          the namespace URI of the Security Layer command
 +     * @param localname
 +     *          the localname of the Security Layer command
 +     * @param slCommandClass
 +     *          the implementation class, or <code>null</code> to deregister a
 +     *          currently registered class
 +     */
 +    public static void putImplClass(String namespaceUri, String localname,
 +      Class<? extends SLCommand> slCommandClass) {
 +        if (slCommandClass != null) {
 +            slRequestTypeMap.put(namespaceUri + ":" + localname, slCommandClass);
 +        } else {
 +            slRequestTypeMap.remove(namespaceUri + ":" + localname);
 +        }
 +    }
 +
 +    /**
 +     * Returns the implementation class of an {@link SLCommand} with the given
 +     * <code>name</code>, or <code>null</code> if no such class is registered.
 +     * 
 +     * @param name
 +     *          the <code>QName</code> of the Security Layer command
 +     * @return the implementation class, or <code>null</code> if no class is
 +     *         registered for the given <code>name</code>
 +     */
 +    public static Class<? extends SLCommand> getImplClass(QName name) {
 +        String namespaceURI = name.getNamespaceURI();
 +        String localPart = name.getLocalPart();
 +        return slRequestTypeMap.get(namespaceURI + ":" + localPart);
 +    }
 +
 +    /**
 +     * Sets the schema to validate Security Layer commands with.
 +     * 
 +     * @param slSchema the schema to validate Security Layer commands with
 +     */
 +    public static void setSLSchema(Schema slSchema) {
 +        SLCommandFactory.slSchema = slSchema;
 +    }
 +
 +    /**
 +     * @return the jaxbContext
 +     */
 +    public static JAXBContext getJaxbContext() {
 +        ensureJaxbContext();
 +        return jaxbContext;
 +    }
 +
 +    /**
 +     * @param jaxbContext the jaxbContext to set
 +     */
 +    public static void setJaxbContext(JAXBContext jaxbContext) {
 +        SLCommandFactory.jaxbContext = jaxbContext;
 +    }
 +
 +    /**
 +     * Initialize the JAXBContext.
 +     */
 +    private synchronized static void ensureJaxbContext() {
 +        if (jaxbContext == null) {
 +            try {
 +                String slPkg = at.buergerkarte.namespaces.securitylayer._1.ObjectFactory.class.getPackage().getName();
 +                String xmldsigPkg = org.w3._2000._09.xmldsig_.ObjectFactory.class.getPackage().getName();
 +                setJaxbContext(JAXBContext.newInstance(slPkg + ":" + xmldsigPkg));
 +            } catch (JAXBException e) {
 +                log.error("Failed to setup JAXBContext security layer request.", e);
 +                throw new SLRuntimeException(e);
 +            }
 +        }
 +    }
 +
 +    /**
 +     * Initialize the security layer schema.
 +     */
 +    private synchronized static void ensureSchema() {
 +        if (slSchema == null) {
 +            try {
 +                SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
 +                ClassLoader cl = SLCommandFactory.class.getClassLoader();
 +                Source[] sources = new Source[SCHEMA_FILES.length];
 +                for (int i = 0; i < SCHEMA_FILES.length; i++) {
 +                    String schemaFile = SCHEMA_FILES[i];
 +                    URL schemaURL = cl.getResource(schemaFile);
 +                    if (schemaURL == null) {
 +                        throw new SLRuntimeException("Failed to load schema file " + schemaFile + ".");
 +                    }
 +                    log.debug("Schema location: " + schemaURL);
 +                    sources[i] = new StreamSource(schemaURL.openStream());
 +                }
 +                Schema schema = schemaFactory.newSchema(sources);
 +                log.debug("Schema successfully created.");
 +                SLCommandFactory.setSLSchema(schema);
 +            } catch (SAXException e) {
 +                log.error("Failed to load security layer schema.", e);
 +                throw new SLRuntimeException("Failed to load security layer schema.", e);
 +            } catch (IOException e) {
 +                log.error("Failed to load security layer schema.", e);
 +                throw new SLRuntimeException("Failed to load security layer schema.", e);
 +            }
 +
 +        }
 +    }
 +
 +    /**
 +     * Get an instance of the <code>SLCommandFactory</code>.
 +     */
 +    public synchronized static SLCommandFactory getInstance() {
 +        if (instance == null) {
 +            ensureJaxbContext();
 +            ensureSchema();
 +            instance = new SLCommandFactory();
 +        }
 +        return instance;
 +    }
 +
 +    /**
 +     * Private constructor used by {@link #getInstance()}.
 +     */
 +    private SLCommandFactory() {
 +    }
 +
 +    /**
 +     * Unmarshalls from the given <code>source</code>.
 +     * 
 +     * @see Unmarshaller#unmarshal(Source)
 +     * 
 +     * <em>Note:</em>Could replace JAXB's unmarshal-time validation engine (see commented code), however,
 +     * we need a redirect filter.
 +     * 
 +     * @param source
 +     *          the source to unmarshal from
 +     * @return the object returned by {@link Unmarshaller#unmarshal(Source)}
 +     * @throws SLRequestException
 +     *           if unmarshalling fails
 +     * @throws SLRuntimeException
 +     *           if an unexpected error occurs configuring the unmarshaller or if
 +     *           unmarshalling fails with an unexpected error
 +     */
 +    protected Object unmarshal(Source source) throws SLRuntimeException,
 +      SLRequestException {
 +
 +        Object object;
 +        try {
 +            
 +//            ValidatorHandler validator = slSchema.newValidatorHandler();
 +//            validator.getContentHandler();
 +//            
 +//            SAXParserFactory spf = SAXParserFactory.newInstance();
 +//            spf.setNamespaceAware(true);
 +//            XMLReader saxReader = spf.newSAXParser().getXMLReader();
 +//            //TODO extend validator to implement redirectContentHandler (validate+redirect)
 +//            saxReader.setContentHandler(validator);
 +//            //TODO get a InputSource
 +//            SAXSource saxSource = new SAXSource(saxReader, source);
 +//            
 +//            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
 +//            //turn off duplicate jaxb validation 
 +//            unmarshaller.setSchema(null);
 +//            unmarshaller.setListener(listener);
 +//            unmarshaller.unmarshal(saxSource);
 +            
 +
 +            XMLInputFactory inputFactory = XMLInputFactory.newInstance();
 +            XMLEventReader eventReader = inputFactory.createXMLEventReader(source);
 +            RedirectEventFilter redirectEventFilter = new RedirectEventFilter();
 +            XMLEventReader filteredReader = inputFactory.createFilteredReader(eventReader, redirectEventFilter);
 +
 +            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
 +            unmarshaller.setListener(new RedirectUnmarshallerListener(redirectEventFilter));
 +            if (slSchema != null) {
 +                unmarshaller.setSchema(slSchema);
 +            }
 +            log.trace("Before unmarshal().");
 +            object = unmarshaller.unmarshal(filteredReader);
 +            log.trace("After unmarshal().");
 +        } catch (UnmarshalException e) {
 +            if (log.isDebugEnabled()) {
 +                log.debug("Failed to unmarshall security layer request.", e);
 +            } else {
 +                log.info("Failed to unmarshall security layer request." + e.getMessage());
 +            }
 +            Throwable cause = e.getCause();
 +            if (cause instanceof SAXParseException) {
 +                throw new SLRequestException(3000,
 +                  SLExceptionMessages.EC3000_UNCLASSIFIED, new Object[]{cause.getMessage()});
 +            } else {
 +                throw new SLRequestException(3000,
 +                  SLExceptionMessages.EC3000_UNCLASSIFIED, new Object[]{e});
 +            }
 +        } catch (JAXBException e) {
 +            // unexpected error
 +            log.error("Failed to unmarshall security layer request.", e);
 +            throw new SLRuntimeException(e);
 +        } catch (XMLStreamException e) {
 +            // unexpected error
 +            log.error("Failed to unmarshall security layer request.", e);
 +            throw new SLRuntimeException(e);
 +        }
 +
 +        return object;
 +
 +    }
 +
 +    /**
 +     * Creates a new <code>SLCommand</code> from the given <code>source</code> and
 +     * <code>context</code>.
 +     * 
 +     * @param source
 +     *          the <code>Source</code> to unmarshall from
 +     * @param context
 +     *          the context for the created <code>SLCommand</code>
 +     * @return the <code>SLCommand</code> unmarshalled from the given
 +     *         <code>source</code>
 +     * @throws SLRequestException 
 +     *           if unmarshalling fails
 +     * @throws SLCommandException
 +     *           if command ist not supported
 +     * @throws SLRuntimeException
 +     *           if an unexpected error occurs configuring the unmarshaller, if
 +     *           unmarshalling fails with an unexpected error or if the
 +     *           corresponding <code>SLCommand</code> could not be instantiated
 +     */
 +    @SuppressWarnings("unchecked")
 +    public SLCommand createSLCommand(Source source, SLCommandContext context)
 +      throws SLCommandException, SLRuntimeException, SLRequestException {
 +
 +        Object object = unmarshal(source);
 +        if (!(object instanceof JAXBElement)) {
 +            // invalid request
 +            log.info("Invalid security layer request. " + object.toString());
 +            throw new SLRequestException(3002, SLExceptionMessages.EC3002_INVALID,
 +              new Object[]{object.toString()});
 +        }
 +
 +        QName qName = ((JAXBElement) object).getName();
 +        Class<? extends SLCommand> implClass = getImplClass(qName);
 +        if (implClass == null) {
 +            // command not supported
 +            log.info("Unsupported command received: " + qName.toString());
 +            throw new SLCommandException(4011,
 +              SLExceptionMessages.EC4011_NOTIMPLEMENTED, new Object[]{qName.toString()});
 +        }
 +
 +        // try to instantiate
 +        SLCommand slCommand;
 +        try {
 +            slCommand = implClass.newInstance();
 +            log.debug("SLCommand " + slCommand.getName() + " created.");
 +        } catch (InstantiationException e) {
 +            // unexpected error
 +            log.error("Failed to instantiate security layer command implementation.",
 +              e);
 +            throw new SLRuntimeException(e);
 +        } catch (IllegalAccessException e) {
 +            // unexpected error
 +            log.error("Failed to instantiate security layer command implementation.",
 +              e);
 +            throw new SLRuntimeException(e);
 +        }
 +        slCommand.init(context, (JAXBElement) object);
 +
 +        return slCommand;
 +
 +    }
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandInvoker.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandInvoker.java new file mode 100644 index 00000000..30c6b68f --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandInvoker.java @@ -0,0 +1,45 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands;
 +
 +import at.gv.egiz.bku.slexceptions.SLCanceledException;
 +
 +public interface SLCommandInvoker {
 +
 +  /**
 +   * 
 +   * @param aContext
 +   * @throws SLCanceledException if the security management prevents execution of this command
 +   */
 +  public void invoke(SLSourceContext aContext) throws SLCanceledException;
 +
 +  /**
 +   * 
 +   * @param aContext
 +   * @return
 +   * @throws SLCanceledException if the security management prevents execution of this command
 +   */
 +  public SLResult getResult(SLTargetContext aContext) throws SLCanceledException;
 +
 +  public void setCommand(at.gv.egiz.bku.slcommands.SLCommand aCmd);
 +  
 +  /**
 +   * Prototype creation 
 +   * @return
 +   */
 +  public SLCommandInvoker newInstance();
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLResult.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLResult.java new file mode 100644 index 00000000..7cf43fda --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLResult.java @@ -0,0 +1,44 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands;
 +
 +import javax.xml.transform.Result;
 +import javax.xml.transform.Transformer;
 +import javax.xml.transform.TransformerException;
 +
 +public interface SLResult {
 +  
 +  public static enum SLResultType {BINARY, XML}; 
 +  
 +  public SLResultType getResultType();
 +  
 +  /**
 +   * The MIME Type of the Result.  
 +   * 
 +   * @return may result null if unknown.
 +   */
 +  public String getMimeType();
 +
 +  public void writeTo(Result aResult);
 +  
 +  /**
 +   * 
 +   * @param result
 +   * @param transformer may be null.
 +   */
 +  public void writeTo(Result result, Transformer transformer) throws TransformerException;
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLSourceContext.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLSourceContext.java new file mode 100644 index 00000000..ded55b2a --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLSourceContext.java @@ -0,0 +1,63 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands;
 +
 +import java.security.cert.X509Certificate;
 +
 +import at.gv.egiz.bku.utils.binding.Protocol;
 +
 +
 +public class SLSourceContext {
 +  
 +  private Protocol sourceProtocol;
 +  private boolean sourceIsDataURL;
 +  private X509Certificate sourceCertificate;
 +  private String sourceHTTPReferer;
 +
 +  public Protocol getSourceProtocol() {
 +    return sourceProtocol;
 +  }
 +
 +  public void setSourceProtocol(Protocol sourceProtocol) {
 +    this.sourceProtocol = sourceProtocol;
 +  }
 +
 +  public boolean isSourceIsDataURL() {
 +    return sourceIsDataURL;
 +  }
 +
 +  public void setSourceIsDataURL(boolean sourceIsDataURL) {
 +    this.sourceIsDataURL = sourceIsDataURL;
 +  }
 +
 +  public X509Certificate getSourceCertificate() {
 +    return sourceCertificate;
 +  }
 +
 +  public void setSourceCertificate(X509Certificate sourceCertificate) {
 +    this.sourceCertificate = sourceCertificate;
 +  }
 +
 +  public String getSourceHTTPReferer() {
 +    return sourceHTTPReferer;
 +  }
 +
 +  public void setSourceHTTPReferer(String sourceHTTPReferer) {
 +    this.sourceHTTPReferer = sourceHTTPReferer;
 +  }
 +
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLTargetContext.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLTargetContext.java new file mode 100644 index 00000000..cf800406 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLTargetContext.java @@ -0,0 +1,50 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands;
 +
 +import java.security.cert.X509Certificate;
 +
 +public class SLTargetContext {
 +  private String targetProtocol;
 +  private boolean targetIsDataURL;
 +  private X509Certificate targetCertificate;
 +
 +  public String getTargetProtocol() {
 +    return targetProtocol;
 +  }
 +
 +  public void setTargetProtocol(String targetProtocol) {
 +    this.targetProtocol = targetProtocol;
 +  }
 +
 +  public boolean isTargetIsDataURL() {
 +    return targetIsDataURL;
 +  }
 +
 +  public void setTargetIsDataURL(boolean targetIsDataURL) {
 +    this.targetIsDataURL = targetIsDataURL;
 +  }
 +
 +  public X509Certificate getTargetCertificate() {
 +    return targetCertificate;
 +  }
 +
 +  public void setTargetCertificate(X509Certificate targetCertificate) {
 +    this.targetCertificate = targetCertificate;
 +  }
 +
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureCommandImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureCommandImpl.java new file mode 100644 index 00000000..136fa6f3 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureCommandImpl.java @@ -0,0 +1,229 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl;
 +
 +import java.io.ByteArrayInputStream;
 +import java.security.NoSuchAlgorithmException;
 +import java.security.cert.CertificateException;
 +import java.security.cert.CertificateFactory;
 +import java.security.cert.X509Certificate;
 +import java.util.Collections;
 +import java.util.Date;
 +
 +import javax.xml.crypto.MarshalException;
 +import javax.xml.crypto.URIReferenceException;
 +import javax.xml.crypto.dsig.XMLSignatureException;
 +
 +import org.apache.commons.logging.Log;
 +import org.apache.commons.logging.LogFactory;
 +import org.w3c.dom.ls.DOMImplementationLS;
 +import org.w3c.dom.ls.LSSerializer;
 +
 +import at.buergerkarte.namespaces.securitylayer._1.CreateXMLSignatureRequestType;
 +import at.buergerkarte.namespaces.securitylayer._1.DataObjectInfoType;
 +import at.gv.egiz.bku.slcommands.CreateXMLSignatureCommand;
 +import at.gv.egiz.bku.slcommands.SLCommandContext;
 +import at.gv.egiz.bku.slcommands.SLResult;
 +import at.gv.egiz.bku.slcommands.impl.xsect.AlgorithmMethodFactory;
 +import at.gv.egiz.bku.slcommands.impl.xsect.AlgorithmMethodFactoryImpl;
 +import at.gv.egiz.bku.slcommands.impl.xsect.IdValueFactory;
 +import at.gv.egiz.bku.slcommands.impl.xsect.IdValueFactoryImpl;
 +import at.gv.egiz.bku.slcommands.impl.xsect.Signature;
 +import at.gv.egiz.bku.slexceptions.SLCommandException;
 +import at.gv.egiz.bku.slexceptions.SLRequestException;
 +import at.gv.egiz.dom.DOMUtils;
 +import at.gv.egiz.stal.InfoboxReadRequest;
 +import at.gv.egiz.stal.InfoboxReadResponse;
 +import at.gv.egiz.stal.STALRequest;
 +import at.gv.egiz.stal.STALResponse;
 +
 +/**
 + * This class implements the security layer command <code>CreateXMLSignatureRequest</code>.
 + * 
 + * @author mcentner
 + */
 +public class CreateXMLSignatureCommandImpl extends SLCommandImpl<CreateXMLSignatureRequestType> implements
 +    CreateXMLSignatureCommand {
 +  
 +  /**
 +   * Logging facility.
 +   */
 +  protected static Log log = LogFactory.getLog(CreateXMLSignatureCommandImpl.class);
 +  
 +  /**
 +   * The signing certificate.
 +   */
 +  protected X509Certificate signingCertificate;
 +  
 +  /**
 +   * The keybox identifier of the key used for signing.
 +   */
 +  protected String keyboxIdentifier;
 +  
 +  /**
 +   * The to-be signed signature.
 +   */
 +  protected Signature signature;
 +
 +  @Override
 +  public void init(SLCommandContext ctx, Object unmarshalledRequest)
 +      throws SLCommandException {
 +    super.init(ctx, unmarshalledRequest);
 +  }
 +
 +  @Override
 +  public void prepareXMLSignature() throws SLCommandException, SLRequestException {
 +
 +   CreateXMLSignatureRequestType request = getRequestValue();
 +    
 +    // TODO: make configurable?
 +    IdValueFactory idValueFactory = new IdValueFactoryImpl();
 +    
 +    // TODO: make configurable?
 +    AlgorithmMethodFactory algorithmMethodFactory;
 +    try {
 +      algorithmMethodFactory = new AlgorithmMethodFactoryImpl(signingCertificate);
 +    } catch (NoSuchAlgorithmException e) {
 +      log.error("Failed to get DigestMethod.", e);
 +      throw new SLCommandException(4006);
 +    }
 +    
 +    signature = new Signature(getCmdCtx().getURLDereferencerContext(), idValueFactory, algorithmMethodFactory);
 +
 +    // SigningTime
 +    signature.setSigningTime(new Date());
 +    
 +    // SigningCertificate
 +    signature.setSignerCeritifcate(signingCertificate);
 +    
 +    // SignatureInfo
 +    if (request.getSignatureInfo() != null) {
 +      signature.setSignatureInfo(request.getSignatureInfo());
 +    }
 +    
 +    // DataObjects
 +    for (DataObjectInfoType dataObjectInfo : request.getDataObjectInfo()) {
 +      signature.addDataObject(dataObjectInfo);
 +    }
 +    
 +    signature.buildXMLSignature();
 +    
 +  }
 +
 +  /**
 +   * Gets the signing certificate from STAL.
 +   * 
 +   * @throws SLCommandException
 +   *           if getting the singing certificate fails
 +   */
 +  private void getSigningCertificate() throws SLCommandException {
 +    
 +    CreateXMLSignatureRequestType request = getRequestValue();
 +    keyboxIdentifier = request.getKeyboxIdentifier();
 +    
 +    InfoboxReadRequest stalRequest = new InfoboxReadRequest();
 +    stalRequest.setInfoboxIdentifier(keyboxIdentifier);
 +    
 +    requestSTAL(Collections.singletonList((STALRequest) stalRequest));
 +    
 +    STALResponse stalResponse = stalResponses.next();
 +    
 +    if (stalResponse instanceof InfoboxReadResponse) {
 +      byte[] infobox = ((InfoboxReadResponse) stalResponse).getInfoboxValue();
 +      
 +      try {
 +        CertificateFactory certFactory = CertificateFactory.getInstance("X509");
 +        signingCertificate = (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(infobox));
 +      } catch (CertificateException e) {
 +        log.info("Failed to decode signing certificate.", e);
 +        // TODO: issue appropriate error
 +        throw new SLCommandException(4000);
 +      }
 +      
 +    } else {
 +      log.info("Failed to get signing certificate.");
 +      // TODO: issue appropriate error
 +      throw new SLCommandException(4000);
 +    }
 +    
 +  }
 +
 +  /**
 +   * Signs the signature.
 +   * 
 +   * @throws SLCommandException
 +   *           if signing the signature fails
 +   */
 +  private void signXMLSignature() throws SLCommandException {
 +    
 +    try {
 +      signature.sign(getCmdCtx().getSTAL(), keyboxIdentifier);
 +    } catch (MarshalException e) {
 +      log.error("Failed to marshall XMLSignature.", e);
 +      throw new SLCommandException(4000);
 +    } catch (XMLSignatureException e) {
 +      if (e.getCause() instanceof URIReferenceException) {
 +        URIReferenceException uriReferenceException = (URIReferenceException) e.getCause();
 +        if (uriReferenceException.getCause() instanceof SLCommandException) {
 +          throw (SLCommandException) uriReferenceException.getCause();
 +        }
 +      }
 +      log.error("Failed to sign XMLSignature.", e);
 +      throw new SLCommandException(4000);
 +    }
 +    
 +  }
 +  
 +  @Override
 +  public SLResult execute() {
 +    try {
 +      
 +      // get certificate in order to select appropriate algorithms for hashing and signing
 +      getSigningCertificate();
 +      
 +      // prepare the XMLSignature for signing
 +      prepareXMLSignature();
 +      
 +      // sign the XMLSignature
 +      signXMLSignature();
 +      
 +      if (log.isTraceEnabled()) {
 +        
 +        DOMImplementationLS domImplLS = DOMUtils.getDOMImplementationLS();
 +        LSSerializer serializer = domImplLS.createLSSerializer();
 +        String debugString = serializer.writeToString(signature.getDocument());
 +
 +        log.trace(debugString);
 +        
 +      }
 +      
 +      return new CreateXMLSignatureResultImpl(signature.getDocument());
 +      
 +    } catch (SLCommandException e) {
 +      return new ErrorResultImpl(e);
 +    } catch (SLRequestException e) {
 +      return new ErrorResultImpl(e);
 +    }
 +  }
 +
 +  @Override
 +  public String getName() {
 +    return "CreateXMLSignatureRequest";
 +  }
 +  
 +  
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureResultImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureResultImpl.java new file mode 100644 index 00000000..d2d2e678 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureResultImpl.java @@ -0,0 +1,138 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl;
 +
 +import javax.xml.bind.JAXBContext;
 +import javax.xml.bind.JAXBElement;
 +import javax.xml.bind.JAXBException;
 +import javax.xml.bind.Marshaller;
 +import javax.xml.transform.Result;
 +import javax.xml.transform.Transformer;
 +import javax.xml.transform.TransformerConfigurationException;
 +import javax.xml.transform.TransformerException;
 +import javax.xml.transform.TransformerFactory;
 +import javax.xml.transform.dom.DOMSource;
 +
 +import org.apache.commons.logging.Log;
 +import org.apache.commons.logging.LogFactory;
 +import org.w3c.dom.Document;
 +import org.w3c.dom.DocumentFragment;
 +import org.w3c.dom.Element;
 +import org.w3c.dom.Node;
 +
 +import at.buergerkarte.namespaces.securitylayer._1.CreateXMLSignatureResponseType;
 +import at.buergerkarte.namespaces.securitylayer._1.ObjectFactory;
 +import at.gv.egiz.bku.slcommands.SLCommandFactory;
 +import at.gv.egiz.bku.slexceptions.SLRuntimeException;
 +
 +/**
 + * This calls implements the result of the security layer command <code>CreateXMLSignature</code>.
 + * 
 + * @author mcentner
 + */
 +public class CreateXMLSignatureResultImpl extends SLResultImpl {
 +
 +  /**
 +   * Logging facility.
 +   */
 +  private static Log log = LogFactory.getLog(CreateXMLSignatureResultImpl.class);
 +  
 +  /**
 +   * The document containing the XMLSignature.
 +   */
 +  protected Document doc;
 +  
 +  /**
 +   * Creates a new instance of this CreateXMLSignatureResultImpl with the given
 +   * signature <code>document</code>.
 +   * 
 +   * @param document the signature document
 +   * 
 +   * @throws NullPointerException if <code>document</code> is <code>null</code>
 +   */
 +  public CreateXMLSignatureResultImpl(Document document) {
 +    super();
 +    
 +    if (document == null) {
 +      throw new NullPointerException("Argument 'document' must not be null.");
 +    }
 +    
 +    this.doc = document;
 +    
 +    marshallCreateXMLSignatureResponse();
 +  }
 +
 +  /**
 +   * Marshalls the <code>CreateXMLSignatureResponse</code>. 
 +   */
 +  private void marshallCreateXMLSignatureResponse() {
 +
 +    ObjectFactory factory = new ObjectFactory();
 +    
 +    CreateXMLSignatureResponseType createCreateXMLSignatureResponseType = factory.createCreateXMLSignatureResponseType();
 +    JAXBElement<CreateXMLSignatureResponseType> createCreateXMLSignatureResponse = factory.createCreateXMLSignatureResponse(createCreateXMLSignatureResponseType);
 +
 +    DocumentFragment fragment = doc.createDocumentFragment();
 +    
 +    JAXBContext jaxbContext = SLCommandFactory.getJaxbContext();
 +    try {
 +      Marshaller marshaller = jaxbContext.createMarshaller();
 +      marshaller.marshal(createCreateXMLSignatureResponse, fragment);
 +    } catch (JAXBException e) {
 +      log.error("Failed to marshall 'CreateXMLSignatureResponse'", e);
 +      throw new SLRuntimeException(e);
 +    }
 +
 +    Node child = fragment.getFirstChild();
 +    if (child instanceof Element) {
 +      Node node = doc.replaceChild(child, doc.getDocumentElement());
 +      child.appendChild(node);
 +    }
 +    
 +  }
 +  
 +  @Override
 +  public void writeTo(Result result) {
 +
 +    try {
 +      writeTo(result, null);
 +    } catch (TransformerException e) {
 +      log.error(e);
 +    }
 +
 +  }
 +
 +  /* (non-Javadoc)
 +   * @see at.gv.egiz.bku.slcommands.impl.SLResultImpl#writeTo(javax.xml.transform.Result, javax.xml.transform.Transformer)
 +   */
 +  @Override
 +  public void writeTo(Result result, Transformer transformer) throws TransformerException {
 +
 +    if (transformer == null) {
 +      TransformerFactory transformerFactory = TransformerFactory.newInstance();
 +      try {
 +        transformer = transformerFactory.newTransformer();
 +      } catch (TransformerConfigurationException e) {
 +        log.error("Failed to create Transformer.", e);
 +        throw new SLRuntimeException(e);
 +      }
 +    }
 +    transformer.transform(new DOMSource(doc), result);
 +    
 +  }
 +
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/ErrorResultImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/ErrorResultImpl.java new file mode 100644 index 00000000..555f83bd --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/ErrorResultImpl.java @@ -0,0 +1,60 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl;
 +
 +import at.buergerkarte.namespaces.securitylayer._1.ErrorResponseType;
 +import at.buergerkarte.namespaces.securitylayer._1.ObjectFactory;
 +import at.gv.egiz.bku.slcommands.ErrorResult;
 +import at.gv.egiz.bku.slexceptions.SLException;
 +
 +import javax.xml.transform.Result;
 +
 +/**
 + * This class implements the security layer result <code>ErrorResponse</code>.
 + * 
 + * @author mcentner
 + */
 +public class ErrorResultImpl extends SLResultImpl implements ErrorResult {
 +
 +  /**
 +   * The exception containing information provided in the <code>ErrorResponse</code>.
 +   */
 +  protected SLException slException;
 +
 +  /**
 +   * Creates a new instance of this ErrorResultImpl with the given
 +   * <code>slException</code> containing information provided in the
 +   * <code>ErrorResponse</code>.
 +   * 
 +   * @param slException the exception 
 +   */
 +  public ErrorResultImpl(SLException slException) {
 +    this.slException = slException;
 +  }
 +
 +  @Override
 +  public void writeTo(Result result) {
 +
 +    ObjectFactory factory = new ObjectFactory();
 +    ErrorResponseType responseType = factory.createErrorResponseType();
 +    responseType.setErrorCode(slException.getErrorCode());
 +    responseType.setInfo(slException.getDetailedMsg());
 +    
 +    writeTo(factory.createErrorResponse(responseType), result);
 +    
 +  }
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadCommandImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadCommandImpl.java new file mode 100644 index 00000000..93131cf4 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadCommandImpl.java @@ -0,0 +1,409 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl;
 +
 +import iaik.asn1.CodingException;
 +import iaik.asn1.DerCoder;
 +
 +import java.io.ByteArrayInputStream;
 +import java.io.ByteArrayOutputStream;
 +import java.io.IOException;
 +import java.io.OutputStream;
 +import java.security.cert.CertificateException;
 +import java.security.cert.CertificateFactory;
 +import java.security.cert.X509Certificate;
 +import java.util.ArrayList;
 +import java.util.List;
 +
 +import javax.xml.bind.JAXBElement;
 +import javax.xml.bind.JAXBException;
 +import javax.xml.parsers.DocumentBuilder;
 +import javax.xml.parsers.DocumentBuilderFactory;
 +import javax.xml.parsers.ParserConfigurationException;
 +import javax.xml.transform.Result;
 +import javax.xml.transform.Transformer;
 +import javax.xml.transform.TransformerConfigurationException;
 +import javax.xml.transform.TransformerException;
 +import javax.xml.transform.TransformerFactory;
 +import javax.xml.transform.dom.DOMResult;
 +import javax.xml.transform.dom.DOMSource;
 +import javax.xml.transform.stream.StreamResult;
 +
 +import org.apache.commons.logging.Log;
 +import org.apache.commons.logging.LogFactory;
 +import org.w3c.dom.Document;
 +import org.w3c.dom.Node;
 +
 +import at.buergerkarte.namespaces.personenbindung._20020506_.CompressedIdentityLinkType;
 +import at.buergerkarte.namespaces.securitylayer._1.AnyChildrenType;
 +import at.buergerkarte.namespaces.securitylayer._1.InfoboxReadParamsBinaryFileType;
 +import at.buergerkarte.namespaces.securitylayer._1.InfoboxReadRequestType;
 +import at.gv.egiz.bku.slcommands.InfoboxReadCommand;
 +import at.gv.egiz.bku.slcommands.SLCommand;
 +import at.gv.egiz.bku.slcommands.SLCommandContext;
 +import at.gv.egiz.bku.slcommands.SLResult;
 +import at.gv.egiz.bku.slexceptions.SLCommandException;
 +import at.gv.egiz.bku.slexceptions.SLExceptionMessages;
 +import at.gv.egiz.bku.slexceptions.SLRuntimeException;
 +import at.gv.egiz.idlink.CompressedIdentityLinkFactory;
 +import at.gv.egiz.idlink.IdentityLinkTransformer;
 +import at.gv.egiz.idlink.ans1.IdentityLink;
 +import at.gv.egiz.stal.InfoboxReadRequest;
 +import at.gv.egiz.stal.InfoboxReadResponse;
 +import at.gv.egiz.stal.STALRequest;
 +
 +/**
 + * This class implements the security layer command
 + * <code>InfoboxReadRequest</code>.
 + * <p>
 + * <b>NOTE:</b> Currently the only supported infobox identifier is '
 + * <code>IdentityLink</code>'.
 + * </p>
 + * 
 + * @author mcentner
 + */
 +public class InfoboxReadCommandImpl extends SLCommandImpl<InfoboxReadRequestType> implements
 +    InfoboxReadCommand {
 +  
 +  /**
 +   * Logging facility.
 +   */
 +  protected static Log log = LogFactory.getLog(InfoboxReadCommandImpl.class);
 +
 +  public static final String INFOBOX_IDENTIFIER_CERTIFICATES = "Certificates";
 +  
 +  public static final String BOX_SPECIFIC_PARAMETER_IDENTITY_LINK_DOMAIN_IDENTIFIER = "IdentityLinkDomainIdentifier";
 +  
 +  public static final String INFOBOX_IDENTIFIER_IDENTITY_LINK = "IdentityLink";
 +
 +  /**
 +   * The <code>InfoboxIdentifier</code>
 +   */
 +  protected String infoboxIdentifier;
 +  
 +  /**
 +   * The <code>IdentityLinkDomainIdentifier</code> value of an IdentyLink infobox.
 +   */
 +  protected String identityLinkDomainIdentifier;
 +
 +  /**
 +   * Is content XML entity?
 +   */
 +  protected boolean isXMLEntity;
 +  
 +  @Override
 +  public String getName() {
 +    return "InfoboxReadRequest";
 +  }
 +
 +  /**
 +   * @return the infoboxIdentifier
 +   */
 +  public String getInfoboxIdentifier() {
 +    return infoboxIdentifier;
 +  }
 +
 +  @Override
 +  public void init(SLCommandContext ctx, Object request) throws SLCommandException {
 +    super.init(ctx, request);
 +    
 +    InfoboxReadRequestType req = getRequestValue();
 +    
 +    infoboxIdentifier = req.getInfoboxIdentifier();
 +    
 +    InfoboxReadParamsBinaryFileType binaryFileParameters = req.getBinaryFileParameters();
 +    if (binaryFileParameters != null) {
 +      isXMLEntity = binaryFileParameters.isContentIsXMLEntity();
 +      log.debug("Got ContentIsXMLEntity=" + isXMLEntity + ".");
 +    }
 +    
 +    if (INFOBOX_IDENTIFIER_IDENTITY_LINK.equals(infoboxIdentifier)) {
 +      
 +      if (req.getAssocArrayParameters() != null) {
 +        log.info("Got AssocArrayParameters but Infobox type is BinaryFile.");
 +        throw new SLCommandException(4010);
 +      }
 +      
 +      
 +      AnyChildrenType boxSpecificParameters = req.getBoxSpecificParameters();
 +
 +      if (boxSpecificParameters != null) {
 +        // check BoxSpecificParameters
 +        List<Object> parameter = boxSpecificParameters.getAny();
 +        JAXBElement<?> element;
 +        if (parameter != null 
 +            && parameter.size() == 1 
 +            && parameter.get(0) instanceof JAXBElement<?>
 +            && SLCommand.NAMESPACE_URI.equals((element = (JAXBElement<?>) parameter.get(0)).getName().getNamespaceURI())
 +            && BOX_SPECIFIC_PARAMETER_IDENTITY_LINK_DOMAIN_IDENTIFIER.equals(element.getName().getLocalPart())
 +            && element.getValue() instanceof String) {
 +          identityLinkDomainIdentifier = (String) element.getValue();
 +          log.debug("Got sl:IdentityLinkDomainIdentifier: " + identityLinkDomainIdentifier);
 +        } else {
 +          log.info("Got invalid BoxSpecificParameters.");
 +          throw new SLCommandException(4010);
 +        }
 +      }
 +      
 +    } else {
 +      throw new SLCommandException(4002,
 +          SLExceptionMessages.EC4002_INFOBOX_UNKNOWN,
 +          new Object[] { infoboxIdentifier });
 +    }
 +    
 +  }
 +
 +  @Override
 +  public SLResult execute() {
 +    try {
 +      return readIdentityLink();
 +    } catch (SLCommandException e) {
 +      return new ErrorResultImpl(e);
 +    }
 +  }
 + 
 +  /**
 +   * Gets the IdentitiyLink form the next STAL response.
 +   * 
 +   * @return the IdentityLink
 +   * 
 +   * @throws SLCommandException if getting the IdentitiyLink fails
 +   */
 +  private IdentityLink getIdentityLinkFromResponses() throws SLCommandException {
 +
 +    // IdentityLink
 +    InfoboxReadResponse response;
 +    if (hasNextResponse()) {
 +      response = (InfoboxReadResponse) nextResponse(InfoboxReadResponse.class);
 +      byte[] idLink = response.getInfoboxValue();
 +      try {
 +        return new IdentityLink(DerCoder.decode(idLink));
 +      } catch (CodingException e) {
 +        log.info("Failed to decode infobox '" + INFOBOX_IDENTIFIER_IDENTITY_LINK + "'.", e);
 +        throw new SLCommandException(4000,
 +            SLExceptionMessages.EC4000_UNCLASSIFIED_INFOBOX_INVALID,
 +            new Object[] { INFOBOX_IDENTIFIER_IDENTITY_LINK });
 +      }
 +    } else {
 +      log.info("No infobox '" + INFOBOX_IDENTIFIER_IDENTITY_LINK + "' returned from STAL.");
 +      throw new SLCommandException(4000);
 +    }
 +    
 +  }
 +  
 +  /**
 +   * Gets the list of certificates from the next STAL responses.
 +   * 
 +   * @return the list of certificates
 +   * 
 +   * @throws SLCommandException if getting the list of certificates fails
 +   */
 +  private List<X509Certificate> getCertificatesFromResponses() throws SLCommandException {
 +    
 +    List<X509Certificate> certificates = new ArrayList<X509Certificate>();
 +
 +    CertificateFactory certFactory;
 +    try {
 +      certFactory = CertificateFactory.getInstance("X509");
 +    } catch (CertificateException e) {
 +      // we should always be able to get an X509 certificate factory
 +      log.error("CertificateFactory.getInstance(\"X509\") failed.", e);
 +      throw new SLRuntimeException(e);
 +    }
 +    
 +    InfoboxReadResponse response;
 +    while(hasNextResponse()) {
 +      response = (InfoboxReadResponse) nextResponse(InfoboxReadResponse.class);
 +      byte[] cert = response.getInfoboxValue();
 +      try {
 +        certificates.add((X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(cert)));
 +      } catch (CertificateException e) {
 +        log.info("Failed to decode certificate.", e);
 +        throw new SLCommandException(4000,
 +            SLExceptionMessages.EC4000_UNCLASSIFIED_INFOBOX_INVALID,
 +            new Object[] { INFOBOX_IDENTIFIER_CERTIFICATES });
 +      }
 +    }
 +    
 +    return certificates;
 +
 +  }
 +
 +  /**
 +   * Uses STAL to read the IdentityLink.
 +   * 
 +   * @return the corresponding security layer result
 +   * 
 +   * @throws SLCommandException if reading the IdentityLink fails
 +   */
 +  private SLResult readIdentityLink() throws SLCommandException {
 +    
 +    List<STALRequest> stalRequests = new ArrayList<STALRequest>();
 +
 +    InfoboxReadRequest infoboxReadRequest;
 +    // get raw identity link
 +    infoboxReadRequest = new InfoboxReadRequest();
 +    infoboxReadRequest.setInfoboxIdentifier(INFOBOX_IDENTIFIER_IDENTITY_LINK);
 +    infoboxReadRequest.setDomainIdentifier(identityLinkDomainIdentifier);
 +    stalRequests.add(infoboxReadRequest);
 +    
 +    // get certificates
 +    infoboxReadRequest = new InfoboxReadRequest();
 +    infoboxReadRequest.setInfoboxIdentifier("SecureSignatureKeypair");
 +    stalRequests.add(infoboxReadRequest);
 +    
 +    infoboxReadRequest = new InfoboxReadRequest();
 +    infoboxReadRequest.setInfoboxIdentifier("CertifiedKeypair");
 +    stalRequests.add(infoboxReadRequest);
 +
 +    requestSTAL(stalRequests);
 +
 +    IdentityLink identityLink = getIdentityLinkFromResponses();
 +    List<X509Certificate> certificates = getCertificatesFromResponses();
 +    
 +    
 +    CompressedIdentityLinkFactory idLinkFactory = CompressedIdentityLinkFactory.getInstance();
 +    JAXBElement<CompressedIdentityLinkType> compressedIdentityLink = idLinkFactory
 +        .createCompressedIdentityLink(identityLink, certificates, identityLinkDomainIdentifier);
 +
 +    IdentityLinkTransformer identityLinkTransformer = IdentityLinkTransformer.getInstance();
 +    String issuerTemplate = identityLink.getIssuerTemplate();
 +    
 +    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
 +    DocumentBuilder db;
 +    try {
 +      db = dbf.newDocumentBuilder();
 +    } catch (ParserConfigurationException e) {
 +      log.error("Failed to create XML document.", e);
 +      throw new SLRuntimeException(e);
 +    }
 +    
 +    Document document = db.newDocument();
 +    try {
 +      idLinkFactory.marshallCompressedIdentityLink(compressedIdentityLink, document, null, true);
 +    } catch (JAXBException e) {
 +      log.info("Failed to marshall CompressedIdentityLink.", e);
 +      throw new SLCommandException(4000,
 +          SLExceptionMessages.EC4000_UNCLASSIFIED_INFOBOX_INVALID,
 +          new Object[] { INFOBOX_IDENTIFIER_IDENTITY_LINK });
 +    }
 +    
 +    InfoboxReadResultImpl result = new InfoboxReadResultImpl();
 +    ByteArrayOutputStream resultBytes = null;
 +    Result xmlResult = (isXMLEntity || identityLinkDomainIdentifier != null) 
 +          ? result.getXmlResult(true) 
 +          : new StreamResult((resultBytes = new ByteArrayOutputStream()));
 +    try {
 +      identityLinkTransformer.transformIdLink(issuerTemplate, new DOMSource(document), xmlResult);
 +    } catch (IOException e) {
 +      // we should not get an IOException as we are writing into a DOMResult
 +      throw new SLRuntimeException(e);
 +    } catch (TransformerException e) {
 +      log.info("Faild to transform CompressedIdentityLink.", e);
 +      throw new SLCommandException(4000,
 +          SLExceptionMessages.EC4000_UNCLASSIFIED_IDLINK_TRANSFORMATION_FAILED,
 +          new Object[] { issuerTemplate });
 +    }
 +    
 +    // TODO: Report BUG in IssuerTemplates
 +    // Some IssuerTemplate stylesheets do not consider the pr:Type-Element of the CompressedIdentityLink ...
 +    if (identityLinkDomainIdentifier != null) {
 +      if (xmlResult instanceof DOMResult) {
 +        Node node = ((DOMResult) xmlResult).getNode();
 +        Node nextSibling = ((DOMResult) xmlResult).getNextSibling();
 +        Node idLinkNode;
 +        if (nextSibling != null) {
 +          idLinkNode = nextSibling.getPreviousSibling();
 +        } else if (node != null) {
 +          idLinkNode = node.getFirstChild();
 +        } else {
 +          log
 +              .error("An IdentityLinkDomainIdentifier of '"
 +                  + identityLinkDomainIdentifier
 +                  + "' has been given. However, it cannot be set, as the transformation result does not contain a node.");
 +          throw new SLCommandException(4000,
 +              SLExceptionMessages.EC4000_UNCLASSIFIED_IDLINK_TRANSFORMATION_FAILED,
 +              new Object[] { issuerTemplate });
 +        }
 +        IdentityLinkTransformer.setDomainIdentifier(idLinkNode, identityLinkDomainIdentifier);
 +      } else {
 +        log
 +            .error("An IdentityLinkDomainIdentifier of '"
 +                + identityLinkDomainIdentifier
 +                + "' has been given. However, it cannot be set, as the transformation result is not of type DOM.");
 +        throw new SLCommandException(4000,
 +            SLExceptionMessages.EC4000_UNCLASSIFIED_IDLINK_TRANSFORMATION_FAILED,
 +            new Object[] { issuerTemplate });
 +      }
 +    }
 +    
 +    if (!isXMLEntity) {
 +      if (resultBytes == null) {
 +        resultBytes = new ByteArrayOutputStream();
 +
 +        if (xmlResult instanceof DOMResult) {
 +          Node node = ((DOMResult) xmlResult).getNode();
 +          Node nextSibling = ((DOMResult) xmlResult).getNextSibling();
 +          
 +          DOMSource xmlSource;
 +          if (nextSibling != null) {
 +            xmlSource = new DOMSource(nextSibling.getPreviousSibling());
 +          } else if (node != null) {
 +            xmlSource = new DOMSource(node.getFirstChild());
 +          } else {
 +            log
 +                .error("IssuerTemplate transformation returned no node.");
 +            throw new SLCommandException(4000,
 +                SLExceptionMessages.EC4000_UNCLASSIFIED_IDLINK_TRANSFORMATION_FAILED,
 +                new Object[] { issuerTemplate });
 +          }
 +          TransformerFactory transformerFactory = TransformerFactory.newInstance();
 +          try {
 +            Transformer transformer = transformerFactory.newTransformer();
 +            transformer.transform(xmlSource, new StreamResult(resultBytes));
 +          } catch (TransformerConfigurationException e) {
 +            log.error(e);
 +            throw new SLCommandException(4000,
 +                SLExceptionMessages.EC4000_UNCLASSIFIED_IDLINK_TRANSFORMATION_FAILED,
 +                new Object[] { issuerTemplate });
 +          } catch (TransformerException e) {
 +            log.error(e);
 +            throw new SLCommandException(4000,
 +                SLExceptionMessages.EC4000_UNCLASSIFIED_IDLINK_TRANSFORMATION_FAILED,
 +                new Object[] { issuerTemplate });
 +          }
 +        } else if (xmlResult instanceof StreamResult) {
 +          OutputStream outputStream = ((StreamResult) xmlResult).getOutputStream();
 +          if (outputStream instanceof ByteArrayOutputStream) {
 +            result.setResultBytes(((ByteArrayOutputStream) outputStream).toByteArray());
 +          } else {
 +            log.error("ContentIsXMLEntity is set to 'false'. However, an XMLResult has already been set.");
 +            throw new SLCommandException(4000,
 +                SLExceptionMessages.EC4000_UNCLASSIFIED_IDLINK_TRANSFORMATION_FAILED,
 +                new Object[] { issuerTemplate });
 +          }
 +        }
 +      } else {
 +        result.setResultBytes(resultBytes.toByteArray());
 +      }
 +    }
 +    
 +    
 +    return result;
 +    
 +  }
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadResultImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadResultImpl.java new file mode 100644 index 00000000..6f07338f --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadResultImpl.java @@ -0,0 +1,171 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl;
 +
 +import javax.xml.bind.JAXBContext;
 +import javax.xml.bind.JAXBElement;
 +import javax.xml.bind.JAXBException;
 +import javax.xml.bind.Marshaller;
 +import javax.xml.parsers.DocumentBuilderFactory;
 +import javax.xml.parsers.ParserConfigurationException;
 +import javax.xml.transform.Result;
 +import javax.xml.transform.Transformer;
 +import javax.xml.transform.TransformerConfigurationException;
 +import javax.xml.transform.TransformerException;
 +import javax.xml.transform.TransformerFactory;
 +import javax.xml.transform.dom.DOMResult;
 +import javax.xml.transform.dom.DOMSource;
 +
 +import org.apache.commons.logging.Log;
 +import org.apache.commons.logging.LogFactory;
 +import org.w3c.dom.Document;
 +import org.w3c.dom.NodeList;
 +
 +import at.buergerkarte.namespaces.securitylayer._1.Base64XMLContentType;
 +import at.buergerkarte.namespaces.securitylayer._1.InfoboxReadResponseType;
 +import at.buergerkarte.namespaces.securitylayer._1.ObjectFactory;
 +import at.buergerkarte.namespaces.securitylayer._1.XMLContentType;
 +import at.gv.egiz.bku.slcommands.InfoboxReadResult;
 +import at.gv.egiz.bku.slcommands.SLCommand;
 +import at.gv.egiz.bku.slcommands.SLCommandFactory;
 +import at.gv.egiz.bku.slexceptions.SLRuntimeException;
 +
 +/**
 + * This class implements the result of the security layer command <code>InfoboxReadRequest</code>.
 + * 
 + * @author mcentner
 + */
 +public class InfoboxReadResultImpl extends SLResultImpl implements
 +    InfoboxReadResult {
 +
 +  /**
 +   * Logging facility.
 +   */
 +  protected static Log log = LogFactory.getLog(InfoboxReadResultImpl.class);
 +
 +  /**
 +   * The XML document containing the infobox content.
 +   */
 +  Document xmlDocument;
 +
 +  /**
 +   * Creates the response document from the given <code>binaryContent</code>.
 +   * 
 +   * @param binaryContent the infobox content
 +   * @param preserveSpace the value of the <code>preserveSpace</code> parameter
 +   * 
 +   * @return the created response document
 +   */
 +  private Document createResponseDocument(byte[] binaryContent, boolean preserveSpace) {
 +    
 +    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
 +    Document doc;
 +    try {
 +      doc = dbf.newDocumentBuilder().newDocument();
 +    } catch (ParserConfigurationException e) {
 +      // it should always be possible to create a new Document
 +      log.error("Failed to create XML document.", e);
 +      throw new SLRuntimeException(e);
 +    }
 +
 +    ObjectFactory factory = new ObjectFactory();
 +    
 +    Base64XMLContentType base64XMLContentType = factory.createBase64XMLContentType();
 +    if (binaryContent == null) {
 +      XMLContentType xmlContentType = factory.createXMLContentType();
 +      if (preserveSpace) {
 +        xmlContentType.setSpace("preserve");
 +      }
 +      base64XMLContentType.setXMLContent(xmlContentType);
 +    } else {
 +      base64XMLContentType.setBase64Content(binaryContent);
 +    }
 +    InfoboxReadResponseType infoboxReadResponseType = factory.createInfoboxReadResponseType();
 +    infoboxReadResponseType.setBinaryFileData(base64XMLContentType);
 +    
 +    JAXBElement<InfoboxReadResponseType> infoboxReadResponse = factory.createInfoboxReadResponse(infoboxReadResponseType);
 +    
 +    JAXBContext context = SLCommandFactory.getJaxbContext();
 +    try {
 +      Marshaller marshaller = context.createMarshaller();
 +      marshaller.marshal(infoboxReadResponse, doc);
 +    } catch (JAXBException e) {
 +      log.error("Failed to marshal 'InfoboxReadResponse' document.", e);
 +      throw new SLRuntimeException(e);
 +    }
 +
 +    return doc;
 +    
 +  }
 +  
 +  
 +  /**
 +   * @return an XMLResult for marshalling the infobox to
 +   */
 +  Result getXmlResult(boolean preserveSpace) {
 +    
 +    xmlDocument = createResponseDocument(null, preserveSpace);
 +    
 +    NodeList nodeList = xmlDocument.getElementsByTagNameNS(SLCommand.NAMESPACE_URI, "XMLContent");
 +    return new DOMResult(nodeList.item(0));
 +    
 +  }
 +  
 +  /**
 +   * Creates a new result document for this <code>InfoboxReadResult</code>
 +   * and sets the given <code>resultBytes</code> as content.
 +   * 
 +   * @param resultBytes
 +   */
 +  void setResultBytes(byte[] resultBytes) {
 +    
 +    xmlDocument = createResponseDocument(resultBytes, false);
 +    
 +  }
 +  
 +  @Override
 +  public void writeTo(Result result) {
 +
 +    try {
 +      writeTo(result, null);
 +    } catch (TransformerException e) {
 +      // TODO Auto-generated catch block
 +      e.printStackTrace();
 +    }
 +
 +  }
 +
 +  /* (non-Javadoc)
 +   * @see at.gv.egiz.bku.slcommands.impl.SLResultImpl#writeTo(javax.xml.transform.Result, javax.xml.transform.Transformer)
 +   */
 +  @Override
 +  public void writeTo(Result result, Transformer transformer) throws TransformerException {
 +
 +    if (transformer == null) {
 +      TransformerFactory transformerFactory = TransformerFactory.newInstance();
 +      try {
 +        transformer = transformerFactory.newTransformer();
 +      } catch (TransformerConfigurationException e) {
 +        log.error("Failed to create Transformer.", e);
 +        throw new SLRuntimeException(e);
 +      }
 +    }
 +    transformer.transform(new DOMSource(xmlDocument), result);
 +    
 +  }
 +
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/NullOperationCommandImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/NullOperationCommandImpl.java new file mode 100644 index 00000000..1b6fb237 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/NullOperationCommandImpl.java @@ -0,0 +1,43 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl; + +import at.buergerkarte.namespaces.securitylayer._1.NullOperationRequestType; +import at.gv.egiz.bku.slcommands.NullOperationCommand; +import at.gv.egiz.bku.slcommands.NullOperationResult; +import at.gv.egiz.bku.slcommands.SLResult; + +/** + * This class implements the security layer command <code>NullOperation</code>.  + *  + * @author mcentner + */ +public class NullOperationCommandImpl extends SLCommandImpl<NullOperationRequestType> implements NullOperationCommand { + +  protected static NullOperationResult RESULT = new NullOperationResultImpl(); +   +  @Override +  public SLResult execute() { +    return RESULT; +  } + +  @Override +  public String getName() { +    return "NullOperationRequest"; +  } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/NullOperationResultImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/NullOperationResultImpl.java new file mode 100644 index 00000000..ae1f91ce --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/NullOperationResultImpl.java @@ -0,0 +1,47 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl; + +import javax.xml.bind.JAXBElement; +import javax.xml.transform.Result; + +import at.buergerkarte.namespaces.securitylayer._1.NullOperationResponseType; +import at.buergerkarte.namespaces.securitylayer._1.ObjectFactory; +import at.gv.egiz.bku.slcommands.NullOperationResult; + +/** + * This class represents the result of the security layer command + * <code>NullOperation</code>. + *  + * @author mcentner + */ +public class NullOperationResultImpl extends SLResultImpl implements NullOperationResult { + +  protected static JAXBElement<NullOperationResponseType> RESPONSE; + +  static { +    ObjectFactory factory = new ObjectFactory(); +    NullOperationResponseType type = factory.createNullOperationResponseType(); +    RESPONSE = factory.createNullOperationResponse(type); +  } +   +  @Override +  public void writeTo(Result result) { +    writeTo(RESPONSE, result); +  } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/SLCommandImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/SLCommandImpl.java new file mode 100644 index 00000000..9a3a2984 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/SLCommandImpl.java @@ -0,0 +1,162 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl;
 +
 +import java.util.Iterator;
 +import java.util.List;
 +import java.util.NoSuchElementException;
 +
 +import javax.xml.bind.JAXBElement;
 +
 +import org.apache.commons.logging.Log;
 +import org.apache.commons.logging.LogFactory;
 +
 +import at.gv.egiz.bku.slcommands.SLCommand;
 +import at.gv.egiz.bku.slcommands.SLCommandContext;
 +import at.gv.egiz.bku.slexceptions.SLCommandException;
 +import at.gv.egiz.stal.ErrorResponse;
 +import at.gv.egiz.stal.STAL;
 +import at.gv.egiz.stal.STALRequest;
 +import at.gv.egiz.stal.STALResponse;
 +
 +/**
 + * This class serves as abstract base class for the implementation of a security
 + * layer command.
 + * 
 + * @author mcentner
 + * 
 + * @param <T>
 + *          the type of the corresponding request value
 + */
 +public abstract class SLCommandImpl<T> implements SLCommand {
 +
 +  /**
 +   * The <code>SLCommandContext</code> for this <code>SLCommand</code>.
 +   */
 +  protected SLCommandContext cmdCtx;
 +
 +  /**
 +   * The request element of this command.
 +   */
 +  protected JAXBElement<T> request;
 +
 +  /**
 +   * An iterator over the <code>STALResponse</code>s received in
 +   * {@link SLCommandImpl#requestSTAL(List)}.
 +   */
 +  protected Iterator<STALResponse> stalResponses;
 +
 +  @SuppressWarnings("unchecked")
 +  @Override
 +  public void init(SLCommandContext ctx, Object request)
 +      throws SLCommandException {
 +
 +    this.request = (JAXBElement<T>) request;
 +
 +    this.cmdCtx = ctx;
 +    assert this.cmdCtx != null;
 +
 +  }
 +
 +  /**
 +   * Returns the request value.
 +   * 
 +   * It is a convenience method for <code>request.getValue()</code>.
 +   * 
 +   * @see JAXBElement#getValue()
 +   * @return the request value
 +   */
 +  protected T getRequestValue() {
 +    return request.getValue();
 +  }
 +
 +  /**
 +   * @return the corresponding <code>SLCommandContext</code>
 +   */
 +  protected SLCommandContext getCmdCtx() {
 +    return cmdCtx;
 +  }
 +
 +  /**
 +   * Calls {@link STAL#handleRequest(List)} with the given
 +   * <code>stalRequests</code>.
 +   * 
 +   * @param stalRequests
 +   * @throws SLCommandException
 +   */
 +     protected void requestSTAL(List<STALRequest> stalRequests) throws SLCommandException {
 +    List<STALResponse> responses = cmdCtx.getSTAL().handleRequest(stalRequests);
 +    if (responses == null) {
 +      Log log = LogFactory.getLog(this.getClass());
 +      log.info("Received no responses from STAL.");
 +      throw new SLCommandException(4000);
 +    } else if (responses.size() != stalRequests.size()) {
 +      Log log = LogFactory.getLog(this.getClass());
 +      log.info("Received invalid count of responses from STAL. Expected "
 +          + stalRequests.size() + ", but got " + responses.size() + ".");
 +      // throw new SLCommandException(4000);
 +    }
 +    stalResponses = responses.iterator();
 +  }
 +
 +  /**
 +   * @return <code>true</code> if there are more {@link STALResponse}s to be
 +   *         fetched with {@link #nextResponse(Class)}, or <code>false</code>
 +   *         otherwise.
 +   */
 +  protected boolean hasNextResponse() {
 +    return (stalResponses != null) ? stalResponses.hasNext() : false;
 +  }
 +
 +  /**
 +   * Returns the next response of type <code>responseClass</code> that has been
 +   * received by {@link #requestSTAL(List)}.
 +   * 
 +   * @param responseClass
 +   *          the response must be an instance of
 +   * @return the next response of type <code>responseClass</code>
 +   * 
 +   * @throws NoSuchElementException
 +   *           if there is no more response
 +   * @throws SLCommandException
 +   *           if the next response is of type {@link ErrorResponse} or not of
 +   *           type <code>responseClass</code>
 +   */
 +  protected STALResponse nextResponse(
 +      Class<? extends STALResponse> responseClass) throws SLCommandException {
 +
 +    if (stalResponses == null) {
 +      throw new NoSuchElementException();
 +    }
 +
 +    STALResponse response = stalResponses.next();
 +
 +    if (response instanceof ErrorResponse) {
 +      throw new SLCommandException(((ErrorResponse) response).getErrorCode());
 +    }
 +
 +    if (!(responseClass.isAssignableFrom(response.getClass()))) {
 +      Log log = LogFactory.getLog(this.getClass());
 +      log.info("Received " + response.getClass() + " from STAL but expected "
 +          + responseClass);
 +      throw new SLCommandException(4000);
 +    }
 +
 +    return response;
 +
 +  }
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/SLResultImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/SLResultImpl.java new file mode 100644 index 00000000..a79382b6 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/SLResultImpl.java @@ -0,0 +1,117 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.transform.Result; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.bku.slcommands.SLCommandFactory; +import at.gv.egiz.bku.slcommands.SLResult; + +/** + * This class serves as an abstract base class for the implementation of a + * security layer result. + *  + * @author mcentner + */ +public abstract class SLResultImpl implements SLResult { + +  /** +   * Logging facility. +   */ +  private static Log log = LogFactory.getLog(SLResult.class); + +  /** +   * The security layer result type (default = XML). +   */ +  protected SLResultType resultType = SLResultType.XML; +   +  /** +   * The security layer result MIME-type (default = <code>text/xml</code>). +   */ +  protected String resultingMimeType = "text/xml"; + +  /* (non-Javadoc) +   * @see at.gv.egiz.bku.slcommands.SLResult#getResultType() +   */ +  public SLResultType getResultType() { +    return resultType; +  } + +  /* (non-Javadoc) +   * @see at.gv.egiz.bku.slcommands.SLResult#getMimeType() +   */ +  public String getMimeType() { +    return resultingMimeType; +  } + +  /** +   * Writes the given <code>response</code> to the <code>result</code>. +   *  +   * @param response the security layer response element +   * @param result the result to marshal the response to +   */ +  @SuppressWarnings("unchecked") +  public void writeTo(JAXBElement response, Result result) { + +    try { +      JAXBContext context = SLCommandFactory.getJaxbContext(); +      Marshaller marshaller = context.createMarshaller(); +      marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); +      marshaller.marshal(response, result); +    } catch (JAXBException e) { +      // TODO Add throws clause to interface +      log.fatal("Failed to marshall JAXBElement.", e); +      throw new RuntimeException("Failed to marshall JAXBElement.", e); +    } + +  } + +  /* (non-Javadoc) +   * @see at.gv.egiz.bku.slcommands.SLResult#writeTo(javax.xml.transform.Result, javax.xml.transform.Transformer) +   */ +  @Override +  public void writeTo(Result result, Transformer transformer) throws TransformerException { +    // TODO Auto-generated method stub +    // fixxme: wb added for testing purposes to be completed +    // begin hack +    if (transformer == null) { +      writeTo(result); +      return; +    } +    // just a quick hack to proceed with testing +    ByteArrayOutputStream os = new ByteArrayOutputStream(); +    writeTo(new StreamResult(os)); +    ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); +    transformer.transform(new StreamSource(is), result); +    //end hack +  } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/AlgorithmMethodFactory.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/AlgorithmMethodFactory.java new file mode 100644 index 00000000..d6cbaefa --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/AlgorithmMethodFactory.java @@ -0,0 +1,79 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl.xsect;
 +
 +import java.security.InvalidAlgorithmParameterException;
 +import java.security.NoSuchAlgorithmException;
 +
 +import javax.xml.crypto.AlgorithmMethod;
 +import javax.xml.crypto.dsig.CanonicalizationMethod;
 +import javax.xml.crypto.dsig.DigestMethod;
 +import javax.xml.crypto.dsig.SignatureMethod;
 +
 +/**
 + * A factory for creating {@link AlgorithmMethod}s.
 + * 
 + * @author mcentner
 + */
 +public interface AlgorithmMethodFactory {
 +
 +  /**
 +   * Creates a new DigestMethod for the given <code>signatureContext</code>.
 +   * 
 +   * @param signatureContext
 +   *          the signature context
 +   * 
 +   * @return a DigestMethod for the given <code>signatureContext</code>
 +   * 
 +   * @throws NoSuchAlgorithmException
 +   * @throws InvalidAlgorithmParameterException
 +   */
 +  public DigestMethod createDigestMethod(SignatureContext signatureContext)
 +      throws NoSuchAlgorithmException, InvalidAlgorithmParameterException;
 +
 +  /**
 +   * Creates a new SignatureMethod for the given <code>signatureContext</code>.
 +   * 
 +   * @param signatureContext
 +   *          the signature context
 +   * 
 +   * @return a SignatureMethod for the given <code>signatureContext</code>
 +   * 
 +   * @throws NoSuchAlgorithmException
 +   * @throws InvalidAlgorithmParameterException
 +   */
 +  public SignatureMethod createSignatureMethod(SignatureContext signatureContext)
 +      throws NoSuchAlgorithmException, InvalidAlgorithmParameterException;
 +
 +  /**
 +   * Creates a new CanonicalizationMethod for the given
 +   * <code>signatureContext</code>.
 +   * 
 +   * @param signatureContext
 +   *          the signature context
 +   * 
 +   * @return a CanonicalizationMethod for the given
 +   *         <code>signatureContext</code>
 +   * 
 +   * @throws NoSuchAlgorithmException
 +   * @throws InvalidAlgorithmParameterException
 +   */
 +  public CanonicalizationMethod createCanonicalizationMethod(
 +      SignatureContext signatureContext) throws NoSuchAlgorithmException,
 +      InvalidAlgorithmParameterException;
 +
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/AlgorithmMethodFactoryImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/AlgorithmMethodFactoryImpl.java new file mode 100644 index 00000000..6b963465 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/AlgorithmMethodFactoryImpl.java @@ -0,0 +1,125 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl.xsect;
 +
 +import iaik.xml.crypto.XmldsigMore;
 +
 +import java.security.InvalidAlgorithmParameterException;
 +import java.security.NoSuchAlgorithmException;
 +import java.security.cert.X509Certificate;
 +
 +import javax.xml.crypto.dsig.CanonicalizationMethod;
 +import javax.xml.crypto.dsig.DigestMethod;
 +import javax.xml.crypto.dsig.SignatureMethod;
 +import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
 +import javax.xml.crypto.dsig.spec.DigestMethodParameterSpec;
 +import javax.xml.crypto.dsig.spec.SignatureMethodParameterSpec;
 +
 +/**
 + * An implementation of the AlgorithmMethod factory that uses the signing
 + * certificate to choose appropriate algorithms.
 + * 
 + * @author mcentner
 + */
 +public class AlgorithmMethodFactoryImpl implements AlgorithmMethodFactory {
 +
 +  /**
 +   * The signature algorithm URI.
 +   */
 +  private String signatureAlgorithmURI;
 +
 +  /**
 +   * The algorithm parameters for the signature algorithm.
 +   */
 +  private SignatureMethodParameterSpec signatureMethodParameterSpec;
 +
 +  /**
 +   * Creates a new AlgrithmMethodFactory with the given
 +   * <code>signingCertificate</code>.
 +   * 
 +   * @param siginingCertificate
 +   * 
 +   * @throws NoSuchAlgorithmException
 +   *           if the public key algorithm of the given
 +   *           <code>signingCertificate</code> is not supported
 +   */
 +  public AlgorithmMethodFactoryImpl(X509Certificate siginingCertificate)
 +      throws NoSuchAlgorithmException {
 +
 +    String algorithm = siginingCertificate.getPublicKey().getAlgorithm();
 +
 +    if ("DSA".equals(algorithm)) {
 +      signatureAlgorithmURI = SignatureMethod.DSA_SHA1;
 +    } else if ("RSA".equals(algorithm)) {
 +      signatureAlgorithmURI = SignatureMethod.RSA_SHA1;
 +    } else if (("EC".equals(algorithm)) || ("ECDSA".equals(algorithm))) {
 +      signatureAlgorithmURI = XmldsigMore.SIGNATURE_ECDSA_SHA1;
 +    } else {
 +      throw new NoSuchAlgorithmException("Public key algorithm '" + algorithm
 +          + "' not supported.");
 +    }
 +
 +  }
 +
 +  /*
 +   * (non-Javadoc)
 +   * 
 +   * @seeat.gv.egiz.bku.slcommands.impl.xsect.AlgorithmMethodFactory#
 +   * createCanonicalizationMethod
 +   * (at.gv.egiz.bku.slcommands.impl.xsect.SignatureContext)
 +   */
 +  @Override
 +  public CanonicalizationMethod createCanonicalizationMethod(
 +      SignatureContext signatureContext) throws NoSuchAlgorithmException,
 +      InvalidAlgorithmParameterException {
 +
 +    return signatureContext.getSignatureFactory().newCanonicalizationMethod(
 +        CanonicalizationMethod.EXCLUSIVE, (C14NMethodParameterSpec) null);
 +
 +  }
 +
 +  /*
 +   * (non-Javadoc)
 +   * 
 +   * @see
 +   * at.gv.egiz.bku.slcommands.impl.xsect.AlgorithmMethodFactory#createDigestMethod
 +   * (at.gv.egiz.bku.slcommands.impl.xsect.SignatureContext)
 +   */
 +  @Override
 +  public DigestMethod createDigestMethod(SignatureContext signatureContext)
 +      throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
 +
 +    return signatureContext.getSignatureFactory().newDigestMethod(
 +        DigestMethod.SHA1, (DigestMethodParameterSpec) null);
 +  }
 +
 +  /*
 +   * (non-Javadoc)
 +   * 
 +   * @seeat.gv.egiz.bku.slcommands.impl.xsect.AlgorithmMethodFactory#
 +   * createSignatureMethod
 +   * (at.gv.egiz.bku.slcommands.impl.xsect.SignatureContext)
 +   */
 +  @Override
 +  public SignatureMethod createSignatureMethod(SignatureContext signatureContext)
 +      throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
 +
 +    return signatureContext.getSignatureFactory().newSignatureMethod(
 +        signatureAlgorithmURI, signatureMethodParameterSpec);
 +  }
 +
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/ByteArrayDereferencer.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/ByteArrayDereferencer.java new file mode 100644 index 00000000..a6473a05 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/ByteArrayDereferencer.java @@ -0,0 +1,65 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl.xsect;
 +
 +import java.io.ByteArrayInputStream;
 +
 +import javax.xml.crypto.Data;
 +import javax.xml.crypto.OctetStreamData;
 +import javax.xml.crypto.URIDereferencer;
 +import javax.xml.crypto.URIReference;
 +import javax.xml.crypto.URIReferenceException;
 +import javax.xml.crypto.XMLCryptoContext;
 +
 +/**
 + * An URIDereferencer implementation that dereferences the given
 + * byte array.
 + * 
 + * @author mcentner
 + */
 +public class ByteArrayDereferencer implements URIDereferencer {
 +
 +  /**
 +   * The dereferenced data.
 +   */
 +  protected byte[] dereferencedData;
 +  
 +  /**
 +   * Creates a new instance of this ByteArrayDereferencer with
 +   * the given <code>dereferencedData</code>.
 +   * 
 +   * @param dereferencedData the octets to be returned by {@link #dereference(URIReference, XMLCryptoContext)}
 +   * 
 +   * @throws NullPointerException if <code>dereferencedData</code> is <code>null</code>
 +   */
 +  public ByteArrayDereferencer(byte[] dereferencedData) {
 +    if (dereferencedData == null) {
 +      throw new NullPointerException("Parameter 'dereferencedData' must not be null.");
 +    }
 +    this.dereferencedData = dereferencedData;
 +  }
 +
 +  /* (non-Javadoc)
 +   * @see javax.xml.crypto.URIDereferencer#dereference(javax.xml.crypto.URIReference, javax.xml.crypto.XMLCryptoContext)
 +   */
 +  @Override
 +  public Data dereference(URIReference uriReference, XMLCryptoContext context)
 +      throws URIReferenceException {
 +    return new OctetStreamData(new ByteArrayInputStream(dereferencedData));
 +  }
 +
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/DataObject.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/DataObject.java new file mode 100644 index 00000000..d25f2526 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/DataObject.java @@ -0,0 +1,1006 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl.xsect;
 +
 +import iaik.xml.crypto.dom.DOMCryptoContext;
 +
 +import java.io.ByteArrayInputStream;
 +import java.io.ByteArrayOutputStream;
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.io.SequenceInputStream;
 +import java.io.StringWriter;
 +import java.io.UnsupportedEncodingException;
 +import java.net.URISyntaxException;
 +import java.nio.charset.Charset;
 +import java.security.InvalidAlgorithmParameterException;
 +import java.security.NoSuchAlgorithmException;
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.HashMap;
 +import java.util.List;
 +import java.util.Map;
 +
 +import javax.xml.crypto.MarshalException;
 +import javax.xml.crypto.dom.DOMStructure;
 +import javax.xml.crypto.dsig.CanonicalizationMethod;
 +import javax.xml.crypto.dsig.DigestMethod;
 +import javax.xml.crypto.dsig.Reference;
 +import javax.xml.crypto.dsig.Transform;
 +import javax.xml.crypto.dsig.XMLObject;
 +import javax.xml.crypto.dsig.spec.TransformParameterSpec;
 +import javax.xml.crypto.dsig.spec.XPathFilter2ParameterSpec;
 +import javax.xml.crypto.dsig.spec.XPathType;
 +
 +import org.apache.commons.logging.Log;
 +import org.apache.commons.logging.LogFactory;
 +import org.w3c.dom.DOMConfiguration;
 +import org.w3c.dom.DOMException;
 +import org.w3c.dom.Document;
 +import org.w3c.dom.DocumentFragment;
 +import org.w3c.dom.Element;
 +import org.w3c.dom.Node;
 +import org.w3c.dom.Text;
 +import org.w3c.dom.bootstrap.DOMImplementationRegistry;
 +import org.w3c.dom.ls.DOMImplementationLS;
 +import org.w3c.dom.ls.LSException;
 +import org.w3c.dom.ls.LSInput;
 +import org.w3c.dom.ls.LSOutput;
 +import org.w3c.dom.ls.LSParser;
 +import org.w3c.dom.ls.LSSerializer;
 +
 +import at.buergerkarte.namespaces.securitylayer._1.Base64XMLLocRefOptRefContentType;
 +import at.buergerkarte.namespaces.securitylayer._1.DataObjectInfoType;
 +import at.buergerkarte.namespaces.securitylayer._1.MetaInfoType;
 +import at.buergerkarte.namespaces.securitylayer._1.TransformsInfoType;
 +import at.gv.egiz.bku.binding.HttpUtil;
 +import at.gv.egiz.bku.slexceptions.SLCommandException;
 +import at.gv.egiz.bku.slexceptions.SLRequestException;
 +import at.gv.egiz.bku.slexceptions.SLRuntimeException;
 +import at.gv.egiz.bku.utils.urldereferencer.StreamData;
 +import at.gv.egiz.bku.utils.urldereferencer.URLDereferencer;
 +import at.gv.egiz.dom.DOMUtils;
 +import at.gv.egiz.slbinding.impl.XMLContentType;
 +
 +/**
 + * This class represents a <code>DataObject</code> of an XML-Signature
 + * created by the security layer command <code>CreateXMLSignature</code>.
 + * 
 + * @author mcentner
 + */
 +public class DataObject {
 +
 +  /**
 +   * Logging facility.
 +   */
 +  private static Log log = LogFactory.getLog(DataObject.class);
 +  
 +  /**
 +   * DOM Implementation.
 +   */
 +  private static final String DOM_LS_3_0 = "LS 3.0";
 +
 +  /**
 +   * The array of the default preferred MIME type order.
 +   */
 +  private static final String[] DEFAULT_PREFFERED_MIME_TYPES = 
 +    new String[] {
 +      "application/xhtml+xml", 
 +      "text/plain"
 +    };
 +  
 +  /**
 +   * The DOM implementation used.
 +   */
 +  private DOMImplementationLS domImplLS;
 +
 +  /**
 +   * The signature context.
 +   */
 +  private SignatureContext ctx;
 +  
 +  /**
 +   * The Reference for this DataObject.
 +   */
 +  private XSECTReference reference;
 +  
 +  /**
 +   * The XMLObject for this DataObject.
 +   */
 +  private XMLObject xmlObject;
 +  
 +  /**
 +   * The MIME-Type of the digest input.
 +   */
 +  private String mimeType;
 +  
 +  /**
 +   * An optional description of the digest input.
 +   */
 +  private String description;
 +  
 +  /**
 +   * Creates a new instance.
 +   * 
 +   * @param document the document of the target signature
 +   */
 +  public DataObject(SignatureContext signatureContext) {
 +    this.ctx = signatureContext;
 +    
 +    DOMImplementationRegistry registry;
 +    try {
 +      registry = DOMImplementationRegistry.newInstance();
 +    } catch (Exception e) {
 +      log.error("Failed to get DOMImplementationRegistry.", e);
 +      throw new SLRuntimeException("Failed to get DOMImplementationRegistry.");
 +    }
 +
 +    domImplLS = (DOMImplementationLS) registry.getDOMImplementation(DOM_LS_3_0);
 +    if (domImplLS == null) {
 +      log.error("Failed to get DOMImplementation " + DOM_LS_3_0);
 +      throw new SLRuntimeException("Failed to get DOMImplementation " + DOM_LS_3_0);
 +    }
 +
 +  }
 +
 +  /**
 +   * @return the reference
 +   */
 +  public Reference getReference() {
 +    return reference;
 +  }
 +
 +  /**
 +   * @return the xmlObject
 +   */
 +  public XMLObject getXmlObject() {
 +    return xmlObject;
 +  }
 +
 +  /**
 +   * @return the mimeType
 +   */
 +  public String getMimeType() {
 +    return mimeType;
 +  }
 +
 +  /**
 +   * @return the description
 +   */
 +  public String getDescription() {
 +    return description;
 +  }
 +
 +  /**
 +   * Configures this DataObject with the information provided within the given
 +   * <code>sl:DataObjectInfo</code>.
 +   * 
 +   * @param dataObjectInfo
 +   *          the <code>sl:DataObjectInfo</code>
 +   * 
 +   * @throws SLCommandException
 +   *           if configuring this DataObject with the information provided in
 +   *           the <code>sl:DataObjectInfo</code> fails.
 +   * @throws SLRequestException
 +   *           if the information provided in the <code>sl:DataObjectInfo</code>
 +   *           does not conform to the security layer specification.
 +   * @throws NullPointerException
 +   *           if <code>dataObjectInfo</code> is <code>null</code>
 +   */
 +  public void setDataObjectInfo(DataObjectInfoType dataObjectInfo) throws SLCommandException, SLRequestException {
 +  
 +    Base64XMLLocRefOptRefContentType dataObject = dataObjectInfo.getDataObject();
 +    String structure = dataObjectInfo.getStructure();
 +    
 +    // select and unmarshal an appropriate transformation path if provided
 +    // and set the final data meta information
 +    XSECTTransforms transforms = createTransformsAndSetFinalDataMetaInfo(dataObjectInfo.getTransformsInfo());
 +  
 +    if ("enveloping".equals(structure)) {
 +      
 +      // configure this DataObject as an enveloped DataObject
 +      setEnvelopedDataObject(dataObject, transforms);
 +      
 +    } else if ("detached".equals(structure)) {
 +      
 +      // configure this DataObject as an detached DataObject
 +      setDetachedDataObject(dataObject, transforms);
 +      
 +    } 
 +    // other values are not allowed by the schema and are therefore ignored
 +    
 +  }
 +
 +  /**
 +   * Configures this DataObject as an enveloped DataObject with the information
 +   * provided within the given <code>sl:DataObject</code>.
 +   * 
 +   * @param dataObject
 +   *          the <code>sl:DataObject</code>
 +   * @param transforms
 +   *          an optional <code>Transforms</code> element (may be
 +   *          <code>null</code>)
 +   * 
 +   * @throws SLCommandException
 +   *           if configuring this DataObject with the information provided in
 +   *           the <code>sl:DataObject</code> fails.
 +   * @throws SLRequestException
 +   *           if the information provided in the <code>sl:DataObject</code>
 +   *           does not conform to the security layer specification.
 +   * @throws NullPointerException
 +   *           if <code>dataObject</code> is <code>null</code>
 +   */
 +  private void setEnvelopedDataObject(
 +      Base64XMLLocRefOptRefContentType dataObject, XSECTTransforms transforms)
 +      throws SLCommandException, SLRequestException {
 +    
 +    String reference = dataObject.getReference();
 +    if (reference == null) {
 +      //
 +      // case A
 +      //
 +      // The Reference attribute is not used; the content of sl:DataObject represents the data object. 
 +      // If the data object is XML-coded (the sl:XMLContent element is used in sl:DataObject), then it 
 +      // must be incorporated in the signature structure as parsed XML.
 +      //
 +
 +      if (dataObject.getBase64Content() != null) {
 +
 +        log.debug("Adding DataObject (Base64Content) without a reference URI.");
 +
 +        // create XMLObject
 +        XMLObject xmlObject = createXMLObject(new ByteArrayInputStream(dataObject.getBase64Content()));
 +
 +        setXMLObjectAndReferenceBase64(xmlObject, transforms);
 +        
 +      } else if (dataObject.getXMLContent() != null) {
 +        
 +        log.debug("Adding DataObject (XMLContent) without a reference URI.");
 +
 +        // create XMLObject
 +        DocumentFragment content = parseDataObject((XMLContentType) dataObject.getXMLContent());
 +        XMLObject xmlObject = createXMLObject(content);
 +        
 +        setXMLObjectAndReferenceXML(xmlObject, transforms);
 +        
 +      } else if (dataObject.getLocRefContent() != null) {
 +        
 +        log.debug("Adding DataObject (LocRefContent) without a reference URI.");
 +        
 +        setEnvelopedDataObject(dataObject.getLocRefContent(), transforms);
 +        
 +      } else {
 +        
 +        // not allowed
 +        log.info("XML structure of the command request contains an " +
 +                "invalid combination of optional elements or attributes. " +
 +            "DataObject of structure='enveloped' without a reference must contain content.");
 +        throw new SLRequestException(3003);
 +        
 +      }
 +      
 +    } else {
 +      
 +      if (dataObject.getBase64Content() == null &&
 +          dataObject.getXMLContent() == null &&
 +          dataObject.getLocRefContent() == null) {
 +
 +        //
 +        // case B
 +        //
 +        // The Reference attribute contains a URI that must be resolved by the 
 +        // Citizen Card Environment to obtain the data object. 
 +        // The content of sl:DataObject remains empty
 +        //
 +        
 +        log.debug("Adding DataObject from reference URI '" + reference + "'.");
 +        
 +        setEnvelopedDataObject(reference, transforms);
 +        
 +      } else {
 +        
 +        // not allowed
 +        log.info("XML structure of the command request contains an " +
 +            "invalid combination of optional elements or attributes. " +
 +        "DataObject of structure='enveloped' with reference must not contain content.");
 +        throw new SLRequestException(3003);
 +        
 +      }
 +      
 +      
 +    }
 +    
 +  }
 +
 +  /**
 +   * Configures this DataObject as an enveloped DataObject with the content to
 +   * be dereferenced from the given <code>reference</code>.
 +   * 
 +   * @param reference
 +   *          the <code>reference</code> URI
 +   * @param transforms
 +   *          an optional <code>Transforms</code> element (may be
 +   *          <code>null</code>)
 +   * 
 +   * @throws SLCommandException
 +   *           if dereferencing the given <code>reference</code> fails, or if
 +   *           configuring this DataObject with the data dereferenced from the
 +   *           given <code>reference</code> fails.
 +   * @throws NullPointerException
 +   *           if <code>reference</code> is <code>null</code>
 +   */
 +  private void setEnvelopedDataObject(String reference, XSECTTransforms transforms) throws SLCommandException {
 +
 +    if (reference == null) {
 +      throw new NullPointerException("Argument 'reference' must not be null.");
 +    }
 +    
 +    // dereference URL
 +    URLDereferencer dereferencer = URLDereferencer.getInstance();
 +    
 +    StreamData streamData;
 +    try {
 +      streamData = dereferencer.dereference(reference, ctx.getDereferencerContext());
 +    } catch (IOException e) {
 +      log.info("Failed to dereference XMLObject from '" + reference + "'.", e);
 +      throw new SLCommandException(4110);
 +    }
 +
 +    Node childNode;
 +    
 +    String contentType = streamData.getContentType();
 +    if (contentType.startsWith("text/xml")) {
 +  
 +      // If content type is text/xml parse content.
 +      String charset = HttpUtil.getCharset(contentType, true);
 +      
 +      Document doc = parseDataObject(streamData.getStream(), charset);
 +      
 +      childNode = doc.getDocumentElement();
 +      
 +      if (childNode == null) {
 +        log.info("Failed to parse XMLObject from '" + reference + "'.");
 +        throw new SLCommandException(4111);
 +      }
 +      
 +      XMLObject xmlObject = createXMLObject(childNode);
 +      
 +      setXMLObjectAndReferenceXML(xmlObject, transforms);
 +      
 +    } else {
 +  
 +      // Include content Base64 encoded.
 +      XMLObject xmlObject = createXMLObject(streamData.getStream());
 +      
 +      setXMLObjectAndReferenceBase64(xmlObject, transforms);
 +      
 +    }
 +    
 +  }
 +
 +  /**
 +   * Configures this DataObject as an detached DataObject with the information
 +   * provided in the given <code>sl:DataObject</code> and optionally
 +   * <code>transforms</code>.
 +   * 
 +   * @param dataObject
 +   *          the <code>sl:DataObject</code>
 +   * @param transforms
 +   *          an optional Transforms object, may be <code>null</code>
 +   * 
 +   * @throws SLCommandException
 +   *           if configuring this DataObject with the information provided in
 +   *           the <code>sl:DataObject</code> fails.
 +   * @throws SLRequestException
 +   *           if the information provided in the <code>sl:DataObject</code>
 +   *           does not conform to the security layer specification.
 +   * @throws NullPointerException
 +   *           if <code>dataObject</code> is <code>null</code>
 +   */
 +  private void setDetachedDataObject(
 +      Base64XMLLocRefOptRefContentType dataObject, XSECTTransforms transforms)
 +      throws SLCommandException, SLRequestException {
 +    
 +    String referenceURI = dataObject.getReference();
 +    
 +    if (referenceURI == null) {
 +      
 +      // not allowed
 +      log.info("XML structure of the command request contains an " +
 +          "invalid combination of optional elements or attributes. " +
 +      "DataObject of structure='detached' must contain a reference.");
 +      throw new SLRequestException(3003);
 +
 +    } else {
 +      
 +      DigestMethod dm;
 +      try {
 +        dm = ctx.getAlgorithmMethodFactory().createDigestMethod(ctx);
 +      } catch (NoSuchAlgorithmException e) {
 +        log.error("Failed to get DigestMethod.", e);
 +        throw new SLCommandException(4006);
 +      } catch (InvalidAlgorithmParameterException e) {
 +        log.error("Failed to get DigestMethod.", e);
 +        throw new SLCommandException(4006);
 +      }
 +      
 +      String idValue = ctx.getIdValueFactory().createIdValue("Reference");
 +      
 +      reference = new XSECTReference(referenceURI, dm, transforms, null, idValue);
 +      
 +      // case D:
 +      //
 +      // The Reference attribute contains a URI that is used by the Citizen Card
 +      // Environment to code the reference to the data object as part of the XML
 +      // signature (attribute URI in the dsig:Reference) element. The content of
 +      // sl:DataObject represents the data object.
 +      
 +      if (dataObject.getLocRefContent() != null) {
 +        String locRef = dataObject.getLocRefContent();
 +        try {
 +          this.reference.setDereferencer(new LocRefDereferencer(ctx.getDereferencerContext(), locRef));
 +        } catch (URISyntaxException e) {
 +          log.info("Invalid URI '" + locRef + "' in DataObject.", e);
 +          throw new SLCommandException(4003);
 +        } catch (IllegalArgumentException e) {
 +          log.info("LocRef URI of '" + locRef + "' not supported in DataObject. ", e);
 +          throw new SLCommandException(4003);
 +        }
 +      } else if (dataObject.getBase64Content() != null) {
 +        byte[] base64Content = dataObject.getBase64Content();
 +        this.reference.setDereferencer(new ByteArrayDereferencer(base64Content));
 +      } else if (dataObject.getXMLContent() != null) {
 +        XMLContentType xmlContent = (XMLContentType) dataObject.getXMLContent();
 +        byte[] bytes = xmlContent.getRedirectedStream().toByteArray();
 +        this.reference.setDereferencer(new ByteArrayDereferencer(bytes));
 +      } else {
 +        
 +        // case C:
 +        //
 +        // The Reference attribute contains a URI that must be resolved by the
 +        // Citizen Card Environment to obtain the data object. The Reference
 +        // attribute contains a URI that is used by the Citizen Card Environment
 +        // to code the reference to the data object as part of the XML signature
 +        // (attribute URI in the dsig:Reference) element. The content of
 +        // sl:DataObject remains empty.
 +
 +      }
 +      
 +    }     
 +  }
 +
 +  /**
 +   * Returns the preferred <code>sl:TransformInfo</code> from the given list of
 +   * <code>transformInfos</code>, or <code>null</code> if none of the given
 +   * <code>transformInfos</code> is preferred over the others.
 +   * 
 +   * @param transformsInfos
 +   *          a list of <code>sl:TransformInfo</code>s
 +   * 
 +   * @return the selected <code>sl:TransformInfo</code> or <code>null</code>, if
 +   *         none is preferred over the others
 +   */
 +  private TransformsInfoType selectPreferredTransformsInfo(List<TransformsInfoType> transformsInfos) {
 +    
 +    Map<String, TransformsInfoType> mimeTypes = new HashMap<String, TransformsInfoType>();
 +    
 +    StringBuilder debugString = null;
 +    if (log.isDebugEnabled()) {
 +      debugString = new StringBuilder();
 +      debugString.append("Got " + transformsInfos.size() + " TransformsInfo(s):");
 +    }
 +    
 +    for (TransformsInfoType transformsInfoType : transformsInfos) {
 +      MetaInfoType finalDataMetaInfo = transformsInfoType.getFinalDataMetaInfo();
 +      String mimeType = finalDataMetaInfo.getMimeType();
 +      String description = finalDataMetaInfo.getDescription();
 +      mimeTypes.put(mimeType, transformsInfoType);
 +      if (debugString != null) {
 +        debugString.append("\n FinalDataMetaInfo: MIME-Type=");
 +        debugString.append(mimeType);
 +        if (description != null) {
 +          debugString.append(" ");
 +          debugString.append(description);
 +        }
 +      }
 +    }
 +
 +    if (debugString != null) {
 +      log.debug(debugString);
 +    }
 +
 +    // look for preferred transform
 +    for (String mimeType : DEFAULT_PREFFERED_MIME_TYPES) {
 +      if (mimeTypes.containsKey(mimeType)) {
 +        return mimeTypes.get(mimeType);
 +      }
 +    }
 +    
 +    // no preferred transform
 +    return null;
 +    
 +  }
 +
 +  /**
 +   * Create an instance of <code>ds:Transforms</code> from the given
 +   * <code>sl:TransformsInfo</code>.
 +   * 
 +   * @param transformsInfo
 +   *          the <code>sl:TransformsInfo</code>
 +   * 
 +   * @return a corresponding unmarshalled <code>ds:Transforms</code>, or
 +   *         <code>null</code> if the given <code>sl:TransformsInfo</code> does
 +   *         not contain a <code>dsig:Transforms</code> element
 +   * 
 +   * @throws SLRequestException
 +   *           if the <code>ds:Transforms</code> in the given
 +   *           <code>transformsInfo</code> are not valid or cannot be parsed.
 +   * 
 +   * @throws MarshalException
 +   *           if the <code>ds:Transforms</code> in the given
 +   *           <code>transformsInfo</code> cannot be unmarshalled.
 +   */
 +  private XSECTTransforms createTransforms(TransformsInfoType transformsInfo) throws SLRequestException, MarshalException {
 +    
 +    ByteArrayOutputStream redirectedStream = ((at.gv.egiz.slbinding.impl.TransformsInfoType) transformsInfo).getRedirectedStream();
 +    byte[] transformBytes = (redirectedStream != null) ? redirectedStream.toByteArray() : null;
 +    
 +    if (transformBytes != null && transformBytes.length > 0) {
 +
 +      // debug
 +      if (log.isTraceEnabled()) {
 +        StringBuilder sb = new StringBuilder();
 +        sb.append("Trying to parse transforms:\n");
 +        sb.append(new String(transformBytes, Charset.forName("UTF-8")));
 +        log.trace(sb);
 +      }
 +      
 +      DOMImplementationLS domImplLS = DOMUtils.getDOMImplementationLS();
 +      LSInput input = domImplLS.createLSInput();
 +      input.setByteStream(new ByteArrayInputStream(transformBytes));
 +
 +      LSParser parser = domImplLS.createLSParser(
 +          DOMImplementationLS.MODE_SYNCHRONOUS, null);
 +      DOMConfiguration domConfig = parser.getDomConfig();
 +      SimpleDOMErrorHandler errorHandler = new SimpleDOMErrorHandler();
 +      domConfig.setParameter("error-handler", errorHandler);
 +      domConfig.setParameter("validate", Boolean.FALSE);
 +
 +      Document document;
 +      try {
 +        document = parser.parse(input);
 +      } catch (DOMException e) {
 +        log.info("Failed to parse dsig:Transforms.", e);
 +        throw new SLRequestException(3002);
 +      } catch (LSException e) {
 +        log.info("Failed to parse dsig:Transforms.", e);
 +        throw new SLRequestException(3002);
 +      }
 +
 +      // adopt ds:Transforms
 +      Element documentElement = document.getDocumentElement();
 +      Node adoptedTransforms = ctx.getDocument().adoptNode(documentElement);
 +
 +      DOMCryptoContext context = new DOMCryptoContext();
 +
 +      // unmarshall ds:Transforms
 +      return new XSECTTransforms(context, adoptedTransforms);
 +
 +    } else {
 +      return null;
 +    }
 +    
 +  }
 +  
 +  /**
 +   * Sets the <code>mimeType</code> and the <code>description</code> value
 +   * for this DataObject.
 +   * 
 +   * @param metaInfoType the <code>sl:FinalMetaDataInfo</code>
 +   * 
 +   * @throws NullPointerException if <code>metaInfoType</code> is <code>null</code>
 +   */
 +  private void setFinalDataMetaInfo(MetaInfoType metaInfoType) {
 +    
 +    this.mimeType = metaInfoType.getMimeType();
 +    this.description = metaInfoType.getDescription();
 +    
 +  }
 +  
 +  /**
 +   * Selects an appropriate transformation path (if present) from the given list
 +   * of <code>sl:TransformInfos</code>, sets the corresponding final data meta info and
 +   * returns the corresponding unmarshalled <code>ds:Transforms</code>.
 +   * 
 +   * @param transformsInfos the <code>sl:TransformInfos</code>
 +   * 
 +   * @return the unmarshalled <code>ds:Transforms</code>, or <code>null</code> if
 +   * no transformation path has been selected.
 +   * 
 +   * @throws SLRequestException if the given list <code>ds:TransformsInfo</code> contains
 +   * an invalid <code>ds:Transforms</code> element, or no suitable transformation path
 +   * can be found. 
 +   */
 +  private XSECTTransforms createTransformsAndSetFinalDataMetaInfo(
 +      List<TransformsInfoType> transformsInfos) throws SLRequestException {
 +
 +    TransformsInfoType preferredTransformsInfo = selectPreferredTransformsInfo(transformsInfos);
 +    // try preferred transform
 +    if (preferredTransformsInfo != null) {
 +
 +      try {
 +        XSECTTransforms transforms = createTransforms(preferredTransformsInfo);
 +        setFinalDataMetaInfo(preferredTransformsInfo.getFinalDataMetaInfo());
 +        return transforms;
 +      } catch (MarshalException e) {
 +      
 +        String mimeType = preferredTransformsInfo.getFinalDataMetaInfo().getMimeType();
 +        log.info("Failed to unmarshal preferred transformation path (MIME-Type="
 +            + mimeType + ").", e);
 +      
 +      }
 +
 +    }
 +
 +    // look for another suitable transformation path
 +    for (TransformsInfoType transformsInfoType : transformsInfos) {
 +      
 +      try {
 +        XSECTTransforms transforms = createTransforms(transformsInfoType);
 +        setFinalDataMetaInfo(transformsInfoType.getFinalDataMetaInfo());
 +        return transforms;
 +      } catch (MarshalException e) {
 +
 +        String mimeType = transformsInfoType.getFinalDataMetaInfo().getMimeType();
 +        log.info("Failed to unmarshal transformation path (MIME-Type="
 +            + mimeType + ").", e);
 +      }
 +      
 +    }
 +
 +    // no suitable transformation path found
 +    throw new SLRequestException(3003);
 +    
 +  }
 +
 +  /**
 +   * Create an XMLObject with the Base64 encoding of the given
 +   * <code>content</code>.
 +   * 
 +   * @param content
 +   *          the to-be Base64 encoded content
 +   * @return an XMLObject with the Base64 encoded <code>content</code>
 +   */
 +  private XMLObject createXMLObject(InputStream content) {
 +    
 +    Text textNode;
 +    try {
 +      textNode = at.gv.egiz.dom.DOMUtils.createBase64Text(content, ctx.getDocument());
 +    } catch (IOException e) {
 +      log.error(e);
 +      throw new SLRuntimeException(e);
 +    }
 +
 +    DOMStructure structure = new DOMStructure(textNode);
 +    
 +    String idValue = ctx.getIdValueFactory().createIdValue("Object");
 +
 +    return ctx.getSignatureFactory().newXMLObject(Collections.singletonList(structure), idValue, null, null);
 +    
 +  }
 +  
 +  /**
 +   * Create an XMLObject with the given <code>content</code> node.
 +   * 
 +   * @param content the content node
 +   * 
 +   * @return an XMLObject with the given <code>content</code>
 +   */
 +  private XMLObject createXMLObject(Node content) {
 +    
 +    String idValue = ctx.getIdValueFactory().createIdValue("Object");
 +    
 +    List<DOMStructure> structures = Collections.singletonList(new DOMStructure(content));
 +    
 +    return ctx.getSignatureFactory().newXMLObject(structures, idValue, null, null);
 +    
 +  }
 +
 +  /**
 +   * Sets the given <code>xmlObject</code> and creates and sets a corresponding
 +   * <code>Reference</code>.
 +   * <p>
 +   * A transform to Base64-decode the xmlObject's content is inserted at the top
 +   * of to the optional <code>transforms</code> if given, or to a newly created
 +   * <code>Transforms</code> element if <code>transforms</code> is
 +   * <code>null</code>.
 +   * 
 +   * @param xmlObject
 +   *          the XMLObject
 +   * @param transforms
 +   *          an optional <code>Transforms</code> element (may be
 +   *          <code>null</code>)
 +   * 
 +   * @throws SLCommandException
 +   *           if creating the Reference fails
 +   * @throws NullPointerException
 +   *           if <code>xmlObject</code> is <code>null</code>
 +   */
 +  private void setXMLObjectAndReferenceBase64(XMLObject xmlObject, XSECTTransforms transforms) throws SLCommandException {
 +    
 +    // create reference URI
 +    //
 +    // NOTE: the ds:Object can be referenced directly, as the Base64 transform
 +    // operates on the text() of the input nodelist.
 +    //
 +    String referenceURI = "#" + xmlObject.getId();
 +  
 +    // create Base64 Transform
 +    Transform transform;
 +    try {
 +      transform = ctx.getSignatureFactory().newTransform(Transform.BASE64, (TransformParameterSpec) null);
 +    } catch (NoSuchAlgorithmException e) {
 +      // algorithm must be present
 +      throw new SLRuntimeException(e);
 +    } catch (InvalidAlgorithmParameterException e) {
 +      // algorithm does not take parameters
 +      throw new SLRuntimeException(e);
 +    }
 +    
 +    if (transforms == null) {
 +      transforms = new XSECTTransforms(Collections.singletonList(transform)); 
 +    } else {
 +      transforms.insertTransform(transform);
 +    }
 +  
 +    DigestMethod dm;
 +    try {
 +      dm = ctx.getAlgorithmMethodFactory().createDigestMethod(ctx);
 +    } catch (NoSuchAlgorithmException e) {
 +      log.error("Failed to get DigestMethod.", e);
 +      throw new SLCommandException(4006);
 +    } catch (InvalidAlgorithmParameterException e) {
 +      log.error("Failed to get DigestMethod.", e);
 +      throw new SLCommandException(4006);
 +    }
 +    String id = ctx.getIdValueFactory().createIdValue("Reference");
 +    
 +    this.xmlObject = xmlObject;
 +    this.reference = new XSECTReference(referenceURI, dm, transforms, null, id);
 +    
 +  }
 +
 +  /**
 +   * Sets the given <code>xmlObject</code> and creates and sets a corresponding
 +   * <code>Reference</code>.
 +   * <p>
 +   * A transform to select the xmlObject's content is inserted at the top of to
 +   * the optional <code>transforms</code> if given, or to a newly created
 +   * <code>Transforms</code> element if <code>transforms</code> is
 +   * <code>null</code>.
 +   * </p>
 +   * 
 +   * @param xmlObject
 +   *          the XMLObject
 +   * @param transforms
 +   *          an optional <code>Transforms</code> element (may be
 +   *          <code>null</code>)
 +   * 
 +   * @throws SLCommandException
 +   *           if creating the Reference fails
 +   * @throws NullPointerException
 +   *           if <code>xmlObject</code> is <code>null</code>
 +   */
 +  private void setXMLObjectAndReferenceXML(XMLObject xmlObject, XSECTTransforms transforms) throws SLCommandException {
 +    
 +    // create reference URI
 +    String referenceURI = "#" + xmlObject.getId();
 +    
 +    // create Transform to select ds:Object's children
 +    Transform xpathTransform;
 +    Transform c14nTransform;
 +    try {
 +
 +      XPathType xpath = new XPathType("id(\"" + xmlObject.getId() + "\")/node()", XPathType.Filter.INTERSECT);
 +      List<XPathType> xpaths = Collections.singletonList(xpath);
 +      XPathFilter2ParameterSpec params = new XPathFilter2ParameterSpec(xpaths);
 +
 +      xpathTransform = ctx.getSignatureFactory().newTransform(Transform.XPATH2, params);
 +
 +      // add exclusive canonicalization to avoid signing the namespace context of the ds:Object
 +      c14nTransform = ctx.getSignatureFactory().newTransform(CanonicalizationMethod.EXCLUSIVE, (TransformParameterSpec) null);
 +      
 +    } catch (NoSuchAlgorithmException e) {
 +      // algorithm must be present
 +      throw new SLRuntimeException(e);
 +    } catch (InvalidAlgorithmParameterException e) {
 +      // params must be appropriate
 +      throw new SLRuntimeException(e);
 +    }
 +  
 +    if (transforms == null) {
 +      List<Transform> newTransfroms = new ArrayList<Transform>();
 +      newTransfroms.add(xpathTransform);
 +      newTransfroms.add(c14nTransform);
 +      transforms = new XSECTTransforms(newTransfroms);
 +    } else {
 +      transforms.insertTransform(xpathTransform);
 +    }
 +    
 +    DigestMethod dm;
 +    try {
 +      dm = ctx.getAlgorithmMethodFactory().createDigestMethod(ctx);
 +    } catch (NoSuchAlgorithmException e) {
 +      log.error("Failed to get DigestMethod.", e);
 +      throw new SLCommandException(4006);
 +    } catch (InvalidAlgorithmParameterException e) {
 +      log.error("Failed to get DigestMethod.", e);
 +      throw new SLCommandException(4006);
 +    }
 +    String id = ctx.getIdValueFactory().createIdValue("Reference");
 +
 +    this.xmlObject = xmlObject;
 +    this.reference = new XSECTReference(referenceURI, dm, transforms, null, id);
 +    
 +  }
 +
 +  /**
 +   * Parses the given <code>xmlContent</code> and returns a corresponding
 +   * document fragment.
 +   * 
 +   * <p>
 +   * The to-be parsed content is surrounded by <dummy> ... </dummy> elements to
 +   * allow for mixed (e.g. Text and Element) content in XMLContent.
 +   * </p>
 +   * 
 +   * @param xmlContent
 +   *          the XMLContent to-be parsed
 +   * 
 +   * @return a document fragment containing the parsed nodes
 +   * 
 +   * @throws SLCommandException
 +   *           if parsing the given <code>xmlContent</code> fails
 +   * 
 +   * @throws NullPointerException
 +   *           if <code>xmlContent</code> is <code>null</code>
 +   */
 +  private DocumentFragment parseDataObject(XMLContentType xmlContent) throws SLCommandException {
 +    
 +    ByteArrayOutputStream redirectedStream =  xmlContent.getRedirectedStream();
 +  
 +    // Note: We can assume a fixed character encoding of UTF-8 for the
 +    // content of the redirect stream as the content has already been parsed
 +    // and serialized again to the redirect stream.
 +    
 +    List<InputStream> inputStreams = new ArrayList<InputStream>();
 +    try {
 +      // dummy start element
 +      inputStreams.add(new ByteArrayInputStream("<dummy>".getBytes("UTF-8")));
 +  
 +      // content
 +      inputStreams.add(new ByteArrayInputStream(redirectedStream.toByteArray()));
 +      
 +      // dummy end element
 +      inputStreams.add(new ByteArrayInputStream("</dummy>".getBytes("UTF-8")));
 +    } catch (UnsupportedEncodingException e) {
 +      throw new SLRuntimeException(e);
 +    }
 +    
 +    SequenceInputStream inputStream = new SequenceInputStream(Collections.enumeration(inputStreams));
 +  
 +    // parse DataObject
 +    Document doc = parseDataObject(inputStream, "UTF-8");
 +    
 +    Element documentElement = doc.getDocumentElement();
 +  
 +    if (documentElement == null ||
 +        !"dummy".equals(documentElement.getLocalName())) {
 +      log.info("Failed to parse DataObject XMLContent.");
 +      throw new SLCommandException(4111);
 +    }
 +    
 +    DocumentFragment fragment = doc.createDocumentFragment();
 +    while (documentElement.getFirstChild() != null) {
 +      fragment.appendChild(documentElement.getFirstChild());
 +    }
 +    
 +    // log parsed document
 +    if (log.isTraceEnabled()) {
 +      
 +      StringWriter writer = new StringWriter();
 +      
 +      writer.write("DataObject:\n");
 +      
 +      LSOutput output = domImplLS.createLSOutput();
 +      output.setCharacterStream(writer);
 +      output.setEncoding("UTF-8");
 +      LSSerializer serializer = domImplLS.createLSSerializer();
 +      serializer.getDomConfig().setParameter("xml-declaration", Boolean.FALSE);
 +      serializer.write(fragment, output);
 +      
 +      log.trace(writer.toString());
 +    }
 +    
 +    return fragment;
 +    
 +  }
 +
 +  /**
 +   * Parses the given <code>inputStream</code> using the given
 +   * <code>encoding</code> and returns the parsed document.
 +   * 
 +   * @param inputStream
 +   *          the to-be parsed input
 +   * 
 +   * @param encoding
 +   *          the encoding to be used for parsing the given
 +   *          <code>inputStream</code>
 +   * 
 +   * @return the parsed document
 +   * 
 +   * @throws SLCommandException
 +   *           if parsing the <code>inputStream</code> fails.
 +   * 
 +   * @throws NullPointerException
 +   *           if <code>inputStram</code> is <code>null</code>
 +   */
 +  private Document parseDataObject(InputStream inputStream, String encoding) throws SLCommandException {
 +
 +    LSInput input = domImplLS.createLSInput();
 +    input.setByteStream(inputStream);
 +
 +    if (encoding != null) {
 +      input.setEncoding(encoding);
 +    }
 +    
 +    LSParser parser = domImplLS.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS, null);
 +    DOMConfiguration domConfig = parser.getDomConfig();
 +    SimpleDOMErrorHandler errorHandler = new SimpleDOMErrorHandler();
 +    domConfig.setParameter("error-handler", errorHandler);
 +    domConfig.setParameter("validate", Boolean.FALSE);
 +
 +    Document doc;
 +    try {
 +      doc = parser.parse(input);
 +    } catch (DOMException e) {
 +      log.info("Existing XML document cannot be parsed.", e);
 +      throw new SLCommandException(4111);
 +    } catch (LSException e) {
 +      log.info("Existing XML document cannot be parsed. ", e);
 +      throw new SLCommandException(4111);
 +    }
 +    
 +    if (errorHandler.hasErrors()) {
 +      // log errors
 +      if (log.isInfoEnabled()) {
 +        List<String> errorMessages = errorHandler.getErrorMessages();
 +        StringBuffer sb = new StringBuffer();
 +        for (String errorMessage : errorMessages) {
 +          sb.append(" ");
 +          sb.append(errorMessage);
 +        }
 +        log.info("Existing XML document cannot be parsed. " + sb.toString());
 +      }
 +      throw new SLCommandException(4111);
 +    }
 +
 +    return doc;
 +    
 +  }
 +
 +
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/IdValueFactory.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/IdValueFactory.java new file mode 100644 index 00000000..df42bd11 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/IdValueFactory.java @@ -0,0 +1,37 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl.xsect;
 +
 +/**
 + * A factory for creating <code>xsd:Id</code>-attribute values.
 + * 
 + * @author mcentner
 + */
 +public interface IdValueFactory {
 +
 +  /**
 +   * Creates a new <code>xsd:Id</code>-attribute value for an Element of the
 +   * given <code>elementName</code>.
 +   * 
 +   * @param elementName
 +   *          the local name of the element to create the value for
 +   * 
 +   * @return a <code>xsd:Id</code>-attribute value
 +   */
 +  public String createIdValue(String elementName);
 +
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/IdValueFactoryImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/IdValueFactoryImpl.java new file mode 100644 index 00000000..b9824655 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/IdValueFactoryImpl.java @@ -0,0 +1,127 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl.xsect;
 +
 +import java.util.HashMap;
 +import java.util.Map;
 +import java.util.Random;
 +
 +/**
 + * An implementation of the IdValueFactory.
 + * <p>
 + * This IdValueFactory creates <code>xsd:Id</code>-attribute values of the form
 + * '<code><elementName>-<random>-<sequenceNumber></code>',
 + * where
 + * <ul>
 + * <li><code><elementName></code> is the name provided at
 + * {@link #createIdValue(String)},</li>
 + * <li><code><random></code> is a random generated fixed value for an
 + * instance of this IdValueFactory and</li>
 + * <li><code><sequenceNumber></code> is the sequence number of the value
 + * generated for a given <code>elementName</code> by an instance of this
 + * IdValueFactory.</li>
 + * </ul>
 + * </p>
 + * 
 + * @author mcentner
 + */
 +public class IdValueFactoryImpl implements IdValueFactory {
 +
 +  /**
 +   * A generator for <code>xsd:Id</code>-attribute values.
 +   * 
 +   * @author mcentner
 +   */
 +  private class IdGenerator {
 +
 +    /**
 +     * The salt.
 +     */
 +    private String salt;
 +
 +    /**
 +     * The element name.
 +     */
 +    private String elementName;
 +
 +    /**
 +     * The sequence number.
 +     */
 +    private int i = 0;
 +
 +    /**
 +     * Creates a new instance of this IdGenerator with the given
 +     * <code>elementName</code> and <code>salt</code> value.
 +     * 
 +     * @param elementName the element name
 +     * @param salt the salt valeu
 +     */
 +    private IdGenerator(String elementName, String salt) {
 +      super();
 +      this.elementName = elementName;
 +      this.salt = salt;
 +    }
 +
 +    /**
 +     * @return returns the next <code>xsd:Id</code>-attribute value.
 +     */
 +    public String getNextId() {
 +      return elementName + "-" + salt + "-" + Integer.toString(++i);
 +    }
 +
 +  }
 +
 +  /**
 +   * A map of element names to <code>xsd:Id</code>-value generators.
 +   */
 +  private Map<String, IdGenerator> generators = new HashMap<String, IdGenerator>();
 +
 +  /**
 +   * The seed value.
 +   */
 +  private String seed;
 +
 +  /**
 +   * Creates a new instance of this IdValueFactory.
 +   */
 +  public IdValueFactoryImpl() {
 +
 +    Random random = new Random();
 +    int rand = random.nextInt();
 +    seed = Integer.toHexString(rand);
 +
 +  }
 +
 +  /*
 +   * (non-Javadoc)
 +   * 
 +   * @see
 +   * at.gv.egiz.bku.slcommands.impl.IdValueFactory#createIdValue(java.lang.String
 +   * )
 +   */
 +  public String createIdValue(String elementName) {
 +
 +    IdGenerator generator = generators.get(elementName);
 +    if (generator == null) {
 +      generator = new IdGenerator(elementName, seed);
 +      generators.put(elementName, generator);
 +    }
 +    return generator.getNextId();
 +
 +  }
 +
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/LocRefDereferencer.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/LocRefDereferencer.java new file mode 100644 index 00000000..a6399c9b --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/LocRefDereferencer.java @@ -0,0 +1,113 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl.xsect;
 +
 +import java.io.IOException;
 +import java.net.URI;
 +import java.net.URISyntaxException;
 +
 +import javax.xml.crypto.Data;
 +import javax.xml.crypto.OctetStreamData;
 +import javax.xml.crypto.URIDereferencer;
 +import javax.xml.crypto.URIReference;
 +import javax.xml.crypto.URIReferenceException;
 +import javax.xml.crypto.XMLCryptoContext;
 +
 +import org.apache.commons.logging.Log;
 +import org.apache.commons.logging.LogFactory;
 +
 +import at.gv.egiz.bku.utils.urldereferencer.StreamData;
 +import at.gv.egiz.bku.utils.urldereferencer.URLDereferencer;
 +import at.gv.egiz.bku.utils.urldereferencer.URLDereferencerContext;
 +
 +/**
 + * An URIDereferencer implementation that dereferences <code>LocRef</code>
 + * references.
 + * 
 + * @author mcentner
 + */
 +public class LocRefDereferencer implements URIDereferencer {
 +
 +  /**
 +   * Logging facility.
 +   */
 +  private static Log log = LogFactory.getLog(LocRefDereferencer.class);
 +
 +  /**
 +   * The <code>LocRef</code>-reference to be dereferenced by
 +   * {@link #dereference(URIReference, XMLCryptoContext)}.
 +   */
 +  protected String locRef;
 +
 +  /**
 +   * The context to be used for dereferencing.
 +   */
 +  protected URLDereferencerContext dereferencerContext;
 +
 +  /**
 +   * Creates a new instance of this LocRefDereferencer with the given
 +   * <code>dereferencerContext</code> and <code>locRef</code> reference.
 +   * 
 +   * @param dereferencerContext
 +   *          the context to be used for dereferencing
 +   * @param locRef
 +   *          the <code>LocRef</code>-reference (must be an absolute URI)
 +   * 
 +   * @throws URISyntaxException
 +   *           if <code>LocRef</code> is not an absolute URI
 +   */
 +  public LocRefDereferencer(URLDereferencerContext dereferencerContext,
 +      String locRef) throws URISyntaxException {
 +
 +    this.dereferencerContext = dereferencerContext;
 +
 +    URI locRefUri = new URI(locRef);
 +    if (locRefUri.isAbsolute()) {
 +      this.locRef = locRef;
 +    } else {
 +      throw new IllegalArgumentException(
 +          "Parameter 'locRef' must be an absolut URI.");
 +    }
 +  }
 +
 +  /*
 +   * (non-Javadoc)
 +   * 
 +   * @see
 +   * javax.xml.crypto.URIDereferencer#dereference(javax.xml.crypto.URIReference,
 +   * javax.xml.crypto.XMLCryptoContext)
 +   */
 +  @Override
 +  public Data dereference(URIReference uriReference, XMLCryptoContext context)
 +      throws URIReferenceException {
 +
 +    URLDereferencer dereferencer = URLDereferencer.getInstance();
 +    StreamData streamData;
 +    try {
 +      streamData = dereferencer.dereference(locRef, dereferencerContext);
 +    } catch (IOException e) {
 +      log.info("Failed to dereference URI'" + locRef + "'. " + e.getMessage(),
 +          e);
 +      throw new URIReferenceException("Failed to dereference URI '" + locRef
 +          + "'. " + e.getMessage(), e);
 +    }
 +
 +    return new OctetStreamData(streamData.getStream(), locRef, streamData
 +        .getContentType());
 +  }
 +
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALPrivateKey.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALPrivateKey.java new file mode 100644 index 00000000..64c758c9 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALPrivateKey.java @@ -0,0 +1,122 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl.xsect;
 +
 +import java.security.PrivateKey;
 +
 +import at.gv.egiz.stal.STAL;
 +import at.gv.egiz.stal.HashDataInputCallback;
 +
 +/**
 + * This class implements a private key used by the {@link STALSignature} class. 
 + * 
 + * @author mcentner
 + */
 +public class STALPrivateKey implements PrivateKey {
 +
 +  private static final long serialVersionUID = 1L;
 +
 +  /**
 +   * The STAL implementation.
 +   */
 +  private STAL stal;
 +  
 +  /**
 +   * The callback interface for obtaining the hash input data.
 +   */
 +  private HashDataInputCallback hashDataInputCallback;
 +  
 +  /**
 +   * The keybox identifier.
 +   */
 +  private String keyboxIdentifier;
 +  
 +  /**
 +   * The signature algorithm.
 +   */
 +  private String algorithm;
 +
 +  /**
 +   * Creates a new instance of this <code>STALPrivateKey</code> with the given
 +   * <code>stal</code> implementation, signature <code>algorithm</code>,
 +   * <code>keyboxIdentifier</code> and <code>hashDataInputCallback</code>
 +   * interface.
 +   * 
 +   * @param stal
 +   *          the STAL implementation
 +   * @param algorithm
 +   *          the signature algorithm
 +   * @param keyboxIdentifier
 +   *          the keybox identifier
 +   * @param hashDataInputCallback
 +   *          the interface for obtaining the has input data
 +   */
 +  public STALPrivateKey(STAL stal,
 +      String algorithm, String keyboxIdentifier, HashDataInputCallback hashDataInputCallback) {
 +    super();
 +    this.keyboxIdentifier = keyboxIdentifier;
 +    this.hashDataInputCallback = hashDataInputCallback;
 +    this.stal = stal;
 +    this.algorithm = algorithm;
 +  }
 +
 +  /* (non-Javadoc)
 +   * @see java.security.Key#getAlgorithm()
 +   */
 +  @Override
 +  public String getAlgorithm() {
 +    return algorithm;
 +  }
 +
 +  /* (non-Javadoc)
 +   * @see java.security.Key#getEncoded()
 +   */
 +  @Override
 +  public byte[] getEncoded() {
 +    throw new UnsupportedOperationException("STALPrivateKey does not support the getEncoded() method.");
 +  }
 +
 +  /* (non-Javadoc)
 +   * @see java.security.Key#getFormat()
 +   */
 +  @Override
 +  public String getFormat() {
 +    return null;
 +  }
 +
 +  /**
 +   * @return the STAL implementation
 +   */
 +  public STAL getStal() {
 +    return stal;
 +  }
 +
 +  /**
 +   * @return the interface for obtaining the hash data input
 +   */
 +  public HashDataInputCallback getHashDataInputCallback() {
 +    return hashDataInputCallback;
 +  }
 +
 +  /**
 +   * @return the keybox identifier
 +   */
 +  public String getKeyboxIdentifier() {
 +    return keyboxIdentifier;
 +  }
 +  
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALProvider.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALProvider.java new file mode 100644 index 00000000..0ab30530 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALProvider.java @@ -0,0 +1,64 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl.xsect;
 +
 +import iaik.xml.crypto.XmldsigMore;
 +
 +import java.security.AccessController;
 +import java.security.PrivilegedAction;
 +import java.security.Provider;
 +import java.security.Signature;
 +import java.util.HashMap;
 +import java.util.Map;
 +
 +import javax.xml.crypto.dsig.SignatureMethod;
 +
 +/**
 + * A security provider implementation that provides {@link Signature} implementations
 + * based on STAL.
 + * 
 + * @author mcentner
 + */
 +public class STALProvider extends Provider {
 +
 +  private static final long serialVersionUID = 1L;
 +  
 +  private static String IMPL_PACKAGE_NAME = "at.gv.egiz.bku.slcommands.impl.xsect";
 +  
 +  public STALProvider() {
 +    
 +    super("STAL", 1.0, "Security Token Abstraction Layer Provider");
 +  
 +    final Map<String, String> map = new HashMap<String, String>();
 +
 +    // TODO: register further algorithms
 +    map.put("Signature." + SignatureMethod.RSA_SHA1,
 +        IMPL_PACKAGE_NAME + ".STALSignature");
 +    map.put("Signature." + XmldsigMore.SIGNATURE_ECDSA_SHA1, 
 +        IMPL_PACKAGE_NAME + ".STALSignature");
 +
 +    AccessController.doPrivileged(new PrivilegedAction<Void>() {
 +      @Override
 +      public Void run() {
 +        putAll(map);
 +        return null;
 +      }
 +    });
 +  
 +  }
 +  
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALSignature.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALSignature.java new file mode 100644 index 00000000..f0fcb891 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALSignature.java @@ -0,0 +1,165 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl.xsect;
 +
 +import java.io.ByteArrayOutputStream;
 +import java.security.InvalidKeyException;
 +import java.security.InvalidParameterException;
 +import java.security.PrivateKey;
 +import java.security.PublicKey;
 +import java.security.SignatureException;
 +import java.security.SignatureSpi;
 +import java.util.Collections;
 +import java.util.List;
 +
 +import at.gv.egiz.stal.ErrorResponse;
 +import at.gv.egiz.stal.STAL;
 +import at.gv.egiz.stal.STALRequest;
 +import at.gv.egiz.stal.STALResponse;
 +import at.gv.egiz.stal.SignRequest;
 +import at.gv.egiz.stal.SignResponse;
 +import at.gv.egiz.stal.HashDataInputCallback;
 +
 +/**
 + * A signature service provider implementation that uses STAL to sign.
 + * 
 + * @author mcentner
 + */
 +public class STALSignature extends SignatureSpi {
 +
 +  /**
 +   * The private key.
 +   */
 +  protected STALPrivateKey privateKey;
 +  
 +  /**
 +   * The to-be signed data.
 +   */
 +  protected ByteArrayOutputStream data = new ByteArrayOutputStream();
 +  
 +  /* (non-Javadoc)
 +   * @see java.security.SignatureSpi#engineGetParameter(java.lang.String)
 +   */
 +  @Override
 +  protected Object engineGetParameter(String param)
 +      throws InvalidParameterException {
 +    throw new InvalidParameterException();
 +  }
 +
 +  /* (non-Javadoc)
 +   * @see java.security.SignatureSpi#engineInitSign(java.security.PrivateKey)
 +   */
 +  @Override
 +  protected void engineInitSign(PrivateKey privateKey)
 +      throws InvalidKeyException {
 +
 +    if (!(privateKey instanceof STALPrivateKey)) {
 +      throw new InvalidKeyException("STALSignature supports STALKeys only.");
 +    }
 +    
 +    this.privateKey = (STALPrivateKey) privateKey;
 +
 +  }
 +
 +  /* (non-Javadoc)
 +   * @see java.security.SignatureSpi#engineInitVerify(java.security.PublicKey)
 +   */
 +  @Override
 +  protected void engineInitVerify(PublicKey publicKey)
 +      throws InvalidKeyException {
 +    
 +    throw new UnsupportedOperationException("STALSignature does not support signature verification.");
 +  }
 +
 +  /* (non-Javadoc)
 +   * @see java.security.SignatureSpi#engineSetParameter(java.lang.String, java.lang.Object)
 +   */
 +  @Override
 +  protected void engineSetParameter(String param, Object value)
 +      throws InvalidParameterException {
 +  }
 +
 +  /* (non-Javadoc)
 +   * @see java.security.SignatureSpi#engineSign()
 +   */
 +  @Override
 +  protected byte[] engineSign() throws SignatureException {
 +   
 +    STAL stal = privateKey.getStal();
 +    
 +    if (stal == null) {
 +      throw new SignatureException("STALSignature requires the STALPrivateKey " +
 +      		"to provide a STAL implementation reference.");
 +    }
 +    
 +    HashDataInputCallback signRefDataSupplier = privateKey.getHashDataInputCallback();
 +    
 +    String keyboxIdentifier = privateKey.getKeyboxIdentifier();
 +    
 +    if (keyboxIdentifier == null) {
 +      throw new SignatureException("STALSignature requires the STALPrivateKey " + 
 +          "to provide a KeyboxIdentifier.");
 +    }
 +    
 +    SignRequest signRequest = new SignRequest();
 +    signRequest.setKeyIdentifier(keyboxIdentifier);
 +    signRequest.setSignedInfo(data.toByteArray());
 +    signRequest.setHashDataInput(signRefDataSupplier);
 +    
 +    List<STALResponse> responses = stal.handleRequest(Collections.singletonList((STALRequest) signRequest));
 +    
 +    if (responses == null || responses.size() != 1) {
 +      throw new SignatureException("Failed to access STAL.");
 +    }
 +
 +    STALResponse response = responses.get(0);
 +    if (response instanceof SignResponse) {
 +      return ((SignResponse) response).getSignatureValue();
 +    } else if (response instanceof ErrorResponse) {
 +      throw new STALSignatureException(((ErrorResponse) response).getErrorCode());
 +    } else {
 +      throw new SignatureException("Failed to access STAL.");
 +    }
 +
 +  }
 +
 +  /* (non-Javadoc)
 +   * @see java.security.SignatureSpi#engineUpdate(byte)
 +   */
 +  @Override
 +  protected void engineUpdate(byte b) throws SignatureException {
 +    data.write(b);
 +  }
 +
 +  /* (non-Javadoc)
 +   * @see java.security.SignatureSpi#engineUpdate(byte[], int, int)
 +   */
 +  @Override
 +  protected void engineUpdate(byte[] b, int off, int len)
 +      throws SignatureException {
 +    data.write(b, off, len);
 +  }
 +
 +  /* (non-Javadoc)
 +   * @see java.security.SignatureSpi#engineVerify(byte[])
 +   */
 +  @Override
 +  protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
 +    throw new UnsupportedOperationException("STALSignature des not support signature verification.");
 +  }
 +
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALSignatureException.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALSignatureException.java new file mode 100644 index 00000000..4e86b07c --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALSignatureException.java @@ -0,0 +1,92 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl.xsect;
 +
 +import java.security.SignatureException;
 +
 +/**
 + * A SignatureException thrown by the {@link STALSignature}.
 + * 
 + * @author mcentner
 + */
 +public class STALSignatureException extends SignatureException {
 +
 +  private static final long serialVersionUID = 1L;
 +  
 +  /**
 +   * The STAL error code.
 +   */
 +  private int errorCode;
 +  
 +  /**
 +   * Creates a new instance of this STALSignatureException. 
 +   */
 +  public STALSignatureException() {
 +  }
 +
 +  /**
 +   * Creates a new instance of this STALSigantureException with
 +   * the given <code>errorCode</code>.
 +   * 
 +   * @param errorCode the error code
 +   */
 +  public STALSignatureException(int errorCode) {
 +    this.errorCode = errorCode;
 +  }
 +
 +  /**
 +   * Creates a new instance of this STALSignatureException with
 +   * the given error <code>msg</code>.
 +   * 
 +   * @param msg the error message
 +   * @see SignatureException#SignatureException(String)
 +   */
 +  public STALSignatureException(String msg) {
 +    super(msg);
 +  }
 +
 +  /**
 +   * Creates a new instance of this STALSignatureException with
 +   * the given root <code>cause</code>.
 +   * 
 +   * @param cause the cause
 +   * @see SignatureException#SignatureException(Throwable)
 +   */
 +  public STALSignatureException(Throwable cause) {
 +    super(cause);
 +  }
 +
 +  /**
 +   * Creates a new instance of this STALSignautureException with
 +   * the given error <code>message</code> and root <code>cause</code>.
 +   * 
 +   * @param message the error message
 +   * @param cause the cause
 +   * @see SignatureException#SignatureException(String, Throwable)
 +   */
 +  public STALSignatureException(String message, Throwable cause) {
 +    super(message, cause);
 +  }
 +
 +  /**
 +   * @return the error code
 +   */
 +  public int getErrorCode() {
 +    return errorCode;
 +  }
 +  
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/Signature.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/Signature.java new file mode 100644 index 00000000..94a4a066 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/Signature.java @@ -0,0 +1,935 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl.xsect;
 +
 +import java.io.ByteArrayInputStream;
 +import java.io.ByteArrayOutputStream;
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.io.InputStreamReader;
 +import java.io.StringWriter;
 +import java.io.UnsupportedEncodingException;
 +import java.security.InvalidAlgorithmParameterException;
 +import java.security.NoSuchAlgorithmException;
 +import java.security.PrivateKey;
 +import java.security.cert.X509Certificate;
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.Date;
 +import java.util.HashMap;
 +import java.util.List;
 +import java.util.Map;
 +
 +import javax.xml.bind.JAXBElement;
 +import javax.xml.bind.JAXBException;
 +import javax.xml.crypto.MarshalException;
 +import javax.xml.crypto.dom.DOMStructure;
 +import javax.xml.crypto.dsig.CanonicalizationMethod;
 +import javax.xml.crypto.dsig.DigestMethod;
 +import javax.xml.crypto.dsig.Reference;
 +import javax.xml.crypto.dsig.SignatureMethod;
 +import javax.xml.crypto.dsig.SignedInfo;
 +import javax.xml.crypto.dsig.XMLObject;
 +import javax.xml.crypto.dsig.XMLSignature;
 +import javax.xml.crypto.dsig.XMLSignatureException;
 +import javax.xml.crypto.dsig.XMLSignatureFactory;
 +import javax.xml.crypto.dsig.dom.DOMSignContext;
 +import javax.xml.crypto.dsig.keyinfo.KeyInfo;
 +import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
 +import javax.xml.crypto.dsig.keyinfo.X509Data;
 +import javax.xml.stream.XMLStreamException;
 +
 +import org.apache.commons.logging.Log;
 +import org.apache.commons.logging.LogFactory;
 +import org.etsi.uri._01903.v1_1.DataObjectFormatType;
 +import org.etsi.uri._01903.v1_1.QualifyingPropertiesType;
 +import org.w3c.dom.DOMConfiguration;
 +import org.w3c.dom.DOMException;
 +import org.w3c.dom.Document;
 +import org.w3c.dom.DocumentFragment;
 +import org.w3c.dom.Element;
 +import org.w3c.dom.Node;
 +import org.w3c.dom.NodeList;
 +import org.w3c.dom.ls.DOMImplementationLS;
 +import org.w3c.dom.ls.LSException;
 +import org.w3c.dom.ls.LSInput;
 +import org.w3c.dom.ls.LSOutput;
 +import org.w3c.dom.ls.LSParser;
 +import org.w3c.dom.ls.LSResourceResolver;
 +import org.w3c.dom.ls.LSSerializer;
 +
 +import at.buergerkarte.namespaces.securitylayer._1.Base64XMLLocRefReqRefContentType;
 +import at.buergerkarte.namespaces.securitylayer._1.Base64XMLOptRefContentType;
 +import at.buergerkarte.namespaces.securitylayer._1.DataObjectAssociationType;
 +import at.buergerkarte.namespaces.securitylayer._1.DataObjectInfoType;
 +import at.buergerkarte.namespaces.securitylayer._1.SignatureInfoCreationType;
 +import at.gv.egiz.bku.binding.HttpUtil;
 +import at.gv.egiz.bku.slexceptions.SLCommandException;
 +import at.gv.egiz.bku.slexceptions.SLRequestException;
 +import at.gv.egiz.bku.utils.HexDump;
 +import at.gv.egiz.bku.utils.urldereferencer.StreamData;
 +import at.gv.egiz.bku.utils.urldereferencer.URLDereferencer;
 +import at.gv.egiz.bku.utils.urldereferencer.URLDereferencerContext;
 +import at.gv.egiz.dom.DOMUtils;
 +import at.gv.egiz.slbinding.impl.XMLContentType;
 +import at.gv.egiz.stal.HashDataInputCallback;
 +import at.gv.egiz.stal.STAL;
 +import at.gv.egiz.xades.QualifyingPropertiesException;
 +import at.gv.egiz.xades.QualifyingPropertiesFactory;
 +
 +/**
 + * This class represents an XML-Signature as to be created by the
 + * security layer command <code>CreateXMLSignatureRequest</code>. 
 + * 
 + * @author mcentner
 + */
 +public class Signature implements HashDataInputCallback {
 +  
 +  /**
 +   * Logging facility.
 +   */
 +  private static Log log = LogFactory.getLog(Signature.class);
 +
 +  /**
 +   * The DOM implementation used.
 +   */
 +  private DOMImplementationLS domImplLS;
 +  
 +  /**
 +   * The SignatureContext for the XMLSignature.
 +   */
 +  private SignatureContext ctx;
 +  
 +  /**
 +   * The list of {@link DataObject}s for this signature.
 +   */
 +  private List<DataObject> dataObjects = new ArrayList<DataObject>();
 +  
 +  /**
 +   * A mapping from the <code>Id</code>-attribute values of this signature's 
 +   * <code>ds:Reference</code>s to the corresponding {@link DataObject}s.
 +   */
 +  private Map<String, DataObject> dataObjectReferencIds = new HashMap<String, DataObject>();
 +  
 +  /**
 +   * The SignatureEnvironment for this signature.
 +   */
 +  private SignatureLocation signatureLocation;
 +  
 +  /**
 +   * The XML signature.
 +   */
 +  private XMLSignature xmlSignature;
 +  
 +  /**
 +   * A list of attributes of type <code>xsd:ID</code> to be registered in the {@link DOMSignContext}.
 +   */
 +  private List<IdAttribute> idAttributes = new ArrayList<IdAttribute>();
 +  
 +  /**
 +   * The signer's X509 certificate.
 +   */
 +  private X509Certificate signerCertificate;
 +  
 +  /**
 +   * The signing time.
 +   */
 +  private Date signingTime;
 +  
 +  /**
 +   * Creates a new SLXMLSignature instance.
 +   */
 +  public Signature(URLDereferencerContext dereferencerContext,
 +      IdValueFactory idValueFactory,
 +      AlgorithmMethodFactory algorithmMethodFactory) {
 +    
 +    domImplLS = DOMUtils.getDOMImplementationLS();
 +    
 +    ctx = new SignatureContext();
 +  
 +    ctx.setSignatureFactory(XMLSignatureFactory.getInstance());
 +
 +    ctx.setDereferencerContext(dereferencerContext);
 +    ctx.setIdValueFactory(idValueFactory);
 +    ctx.setAlgorithmMethodFactory(algorithmMethodFactory);
 +    
 +  }
 +
 +  /**
 +   * @return the Document containing this Signature
 +   */
 +  public Document getDocument() {
 +    return ctx.getDocument();
 +  }
 +  
 +  /**
 +   * @return the parent Node for this Signature
 +   */
 +  public Node getParent() {
 +    return (signatureLocation != null) ? signatureLocation.getParent() : null;
 +  }
 +
 +  /**
 +   * @return the next sibling Node for this Signature
 +   */
 +  public Node getNextSibling() {
 +    return (signatureLocation != null) ? signatureLocation.getNextSibling() : null;
 +  }
 +  
 +  /**
 +   * @return the XMLSignature
 +   */
 +  public XMLSignature getXMLSignature() {
 +    return xmlSignature;
 +  }
 +  
 +  /**
 +   * @return the list of {@link Reference}s of this Signature
 +   */
 +  @SuppressWarnings("unchecked")
 +  public List<Reference> getReferences() {
 +    return (xmlSignature != null) ? xmlSignature.getSignedInfo().getReferences() : null;
 +  }
 +  
 +  /**
 +   * @return the list of {@link XMLObject}s of this Signature
 +   */
 +  @SuppressWarnings("unchecked")
 +  public List<XMLObject> getXMLObjects() {
 +    return (xmlSignature != null) ? xmlSignature.getObjects() : null;
 +  }
 +
 +  /**
 +   * Prepares the signature document with the information given by the
 +   * <code>signatureInfo</code> provided.
 +   * 
 +   * @param signatureInfo
 +   *          the <code>SignatureInfo</code>
 +   * 
 +   * @throws SLCommandException
 +   *           if processing fails for any reason
 +   * @throws IllegalStateException
 +   *           if the <code>parent</code> node has already been set
 +   * @throws NullPointerException
 +   *           if <code>signatureInfo</code> is <code>null</code>
 +   */
 +  public void setSignatureInfo(SignatureInfoCreationType signatureInfo) throws SLCommandException {
 +    
 +    if (signatureLocation != null) {
 +      throw new IllegalStateException("SignatureEnvironment already set.");
 +    }
 +    
 +    Base64XMLOptRefContentType signatureEnvironment = signatureInfo.getSignatureEnvironment();
 +    
 +    if (signatureEnvironment == null) {
 +
 +      // no SignatureEnvironment, so we use an empty document and the document as parent
 +      ensureSignatureLocation();
 +      
 +    } else {
 +      
 +      // parse SignatureEnvrionment and use as document
 +      Document document = parseSignatureEnvironment(signatureEnvironment, signatureInfo.getSupplement());
 +      ctx.setDocument(document);
 +
 +      signatureLocation = new SignatureLocation(ctx);
 +      signatureLocation.setSignatureInfo(signatureInfo);
 +      
 +    }
 +    
 +  }
 +
 +  /**
 +   * Ensures a SignatureLocation for this Signature.
 +   */
 +  private void ensureSignatureLocation() {
 +
 +    if (signatureLocation == null) {
 +      Document document = DOMUtils.createDocument();
 +      ctx.setDocument(document);
 +      
 +      signatureLocation = new SignatureLocation(ctx);
 +      signatureLocation.setParent(document);
 +    }
 +    
 +  }
 +
 +  /**
 +   * Adds a DataObject with the information given by the
 +   * <code>dataObjectInfo</code> provided to this Signature.
 +   * 
 +   * @param dataObjectInfo
 +   *          the <code>DataObjectInfo</code> element
 +   * 
 +   * @throws SLCommandException
 +   *           if adding the DataObject fails
 +   * @throws SLRequestException
 +   *           if the information provided by the given
 +   *           <code>dataObjectInfo</code> does not conform to the security
 +   *           layer specification
 +   * @throws NullPointerException
 +   *           if <code>dataObjectInfo</code> is <code>null</code>
 +   */
 +  public void addDataObject(DataObjectInfoType dataObjectInfo) throws SLCommandException, SLRequestException {
 +    
 +    ensureSignatureLocation();
 +    
 +    DataObject dataObject = new DataObject(ctx);
 +    dataObject.setDataObjectInfo(dataObjectInfo);
 +    
 +    dataObjects.add(dataObject);
 +    
 +    dataObjectReferencIds.put(dataObject.getReference().getId(), dataObject);
 +    
 +  }
 +  
 +  /**
 +   * Sets the <code>SigningTime</code> qualifying property of this Signature.
 +   * 
 +   * @param signingTime the signing time to set
 +   */
 +  public void setSigningTime(Date signingTime) {
 +    this.signingTime = signingTime;
 +  }
 +
 +  /**
 +   * Sets the <code>SignerCertificate</code> qualifying property of this Signature.
 +   * 
 +   * @param certificate the signer's certificate
 +   */
 +  public void setSignerCeritifcate(X509Certificate certificate) {
 +    this.signerCertificate = certificate;
 +  }
 +  
 +  /**
 +   * Builds the XMLSignature data structure of this Signature as configured by
 +   * the various setter methods.
 +   * 
 +   * @throws SLCommandException if building this signature fails
 +   */
 +  public void buildXMLSignature() throws SLCommandException {
 +    
 +    List<XMLObject> objects = new ArrayList<XMLObject>();
 +    List<Reference> references = new ArrayList<Reference>();
 +    
 +    // add all data objects
 +    for (DataObject dataObject : dataObjects) {
 +      if (dataObject.getXmlObject() != null) {
 +        objects.add(dataObject.getXmlObject());
 +      }
 +      if (dataObject.getReference() != null) {
 +        references.add(dataObject.getReference());
 +      }
 +    }
 +
 +    addXAdESObjectAndReference(objects, references);
 +    
 +    XMLSignatureFactory signatureFactory = ctx.getSignatureFactory();
 +    AlgorithmMethodFactory algorithmMethodFactory = ctx.getAlgorithmMethodFactory();
 +    
 +    CanonicalizationMethod cm;
 +    SignatureMethod sm;
 +    try {
 +      cm = algorithmMethodFactory.createCanonicalizationMethod(ctx);
 +      sm = algorithmMethodFactory.createSignatureMethod(ctx);
 +    } catch (NoSuchAlgorithmException e) {
 +      log.error("Failed to get Canonicalization or Signature algorithm.", e);
 +      throw new SLCommandException(4006);
 +    } catch (InvalidAlgorithmParameterException e) {
 +      log.error("Failed to get Canonicalization or Signature algorithm.", e);
 +      throw new SLCommandException(4006);
 +    }
 +    
 +    String siId = ctx.getIdValueFactory().createIdValue("SignedInfo");
 +    
 +    SignedInfo si = signatureFactory.newSignedInfo(cm, sm, references, siId);
 +    
 +    KeyInfo ki = null;
 +    if (signerCertificate != null) {
 +      KeyInfoFactory kif = KeyInfoFactory.getInstance();
 +      X509Data x509Data = kif.newX509Data(Collections.singletonList(signerCertificate));
 +      ki = kif.newKeyInfo(Collections.singletonList(x509Data));
 +    }
 +    
 +    String signatureId = ctx.getIdValueFactory().createIdValue("Signature");
 +    String signatureValueId = ctx.getIdValueFactory().createIdValue("SignatureValue");
 +    
 +    xmlSignature = signatureFactory.newXMLSignature(si, ki, objects, signatureId, signatureValueId);
 +    
 +  }
 +
 +  /**
 +   * Sign this Signature using the given <code>signContext</code>.
 +   * <p>
 +   * Call's {@link #buildXMLSignature()} if it has not been called yet.
 +   * </p>
 +   * 
 +   * @param signContext
 +   *          the signing context
 +   * 
 +   * @throws MarshalException
 +   *           if marshalling the XMLSignature fails
 +   * @throws XMLSignatureException
 +   *           if signing the XMLSignature fails
 +   * @throws SLCommandException
 +   *           if building the XMLSignature fails
 +   * @throws NullPointerException
 +   *           if <code>signContext</code> is <code>null</code>
 +   */
 +  public void sign(DOMSignContext signContext) throws MarshalException, XMLSignatureException, SLCommandException {
 +
 +    if (xmlSignature == null) {
 +      buildXMLSignature();
 +    }
 +    
 +    for (IdAttribute idAttribute : idAttributes) {
 +      signContext.setIdAttributeNS(idAttribute.element, idAttribute.namespaceURI, idAttribute.localName);
 +    }
 +    
 +    // DO NOT USE: 
 +    // signContext.setProperty("iaik.xml.crypto.dsig.sign-over", Boolean.TRUE);
 +    
 +    signContext.setProperty("javax.xml.crypto.dsig.cacheReference", Boolean.TRUE);
 +    
 +    signContext.putNamespacePrefix(XMLSignature.XMLNS, "dsig");
 +    
 +    signContext.setURIDereferencer(new URIDereferncerAdapter(ctx.getDereferencerContext()));
 +    
 +    try {
 +      xmlSignature.sign(signContext);
 +    } catch (XMLSignatureException e) {
 +      Throwable cause = e.getCause();
 +      while (cause != null) {
 +        if (cause instanceof STALSignatureException) {
 +          int errorCode = ((STALSignatureException) cause).getErrorCode();
 +          SLCommandException commandException = new SLCommandException(errorCode);
 +          log.info("Failed to sign signature.", commandException);
 +          throw commandException;
 +        } else {
 +          cause = cause.getCause();
 +        }
 +      }
 +      throw e;
 +    }
 +    
 +    // debug
 +    if (log.isTraceEnabled()) {
 +      for (DataObject dataObject : dataObjects) {
 +        Reference reference = dataObject.getReference();
 +        InputStream digestInputStream = reference.getDigestInputStream();
 +        if (digestInputStream != null) {
 +          String mimeType = dataObject.getMimeType();
 +          StringBuilder sb = new StringBuilder();
 +          sb.append("DigestInput for Reference with id='");
 +          sb.append(reference.getId());
 +          sb.append("' (MIME-Type=");
 +          sb.append(dataObject.getMimeType());
 +          sb.append("):\n");
 +          try {
 +            if (mimeType != null && (
 +                mimeType.startsWith("text") ||
 +                "application/xhtml+xml".equals(mimeType))) {
 +              byte[] b = new byte[512];
 +              for (int l; (l = digestInputStream.read(b)) != -1;) {
 +                sb.append(new String(b, 0, l));
 +              }
 +            } else {
 +              sb.append(HexDump.hexDump(digestInputStream));
 +            }
 +          } catch (IOException e) {
 +            log.error(e);
 +          }
 +          log.trace(sb.toString());
 +        } else {
 +          log.trace("Reference caching is not enabled.");
 +        }
 +      }
 +    }
 +    
 +  }
 +
 +  /**
 +   * Sign this Signature using the given <code>stal</code> implementation and
 +   * <code>keyboxIdentifier</code>.
 +   * <p>
 +   * This method configures an appropriate {@link DOMSignContext} and calls
 +   * {@link #sign(DOMSignContext)}. If {@link #buildXMLSignature()} has not been
 +   * called yet, it is called by this method.
 +   * </p>
 +   * 
 +   * @param stal
 +   *          the STAL implementation to use
 +   * @param keyboxIdentifier
 +   *          the KeyboxIdentifier to use
 +   * 
 +   * @throws MarshalException
 +   *           if marshalling this Signature fails
 +   * @throws XMLSignatureException
 +   *           if signing this Signature fails
 +   * @throws SLCommandException
 +   *           if building this Signature fails 
 +   * @throws NullPointerException
 +   *           if <code>stal</code> or <code>keyboxIdentifier</code> is
 +   *           <code>null</code>
 +   */
 +  public void sign(STAL stal, String keyboxIdentifier) throws MarshalException, XMLSignatureException, SLCommandException {
 +
 +    if (stal == null) {
 +      throw new NullPointerException("Argument 'stal' must not be null.");
 +    }
 +    
 +    if (keyboxIdentifier == null) {
 +      throw new NullPointerException("Argument 'keyboxIdentifier' must not be null.");
 +    }
 +    
 +    if (xmlSignature == null) {
 +      buildXMLSignature();
 +    }
 +    
 +    SignatureMethod signatureMethod = xmlSignature.getSignedInfo().getSignatureMethod();
 +    String algorithm = signatureMethod.getAlgorithm();
 +    
 +    PrivateKey privateKey = new STALPrivateKey(stal, algorithm, keyboxIdentifier, this);
 +    
 +    DOMSignContext signContext;
 +    if (getNextSibling() == null) {
 +      signContext = new DOMSignContext(privateKey, getParent());
 +    } else {
 +      signContext = new DOMSignContext(privateKey, getParent(), getNextSibling());
 +    }
 +    
 +    sign(signContext);
 +  }
 +  
 +  @Override
 +  public InputStream getHashDataInput(String referenceId) {
 +    
 +    DataObject dataObject = dataObjectReferencIds.get(referenceId);
 +    if (dataObject != null) {
 +      return dataObject.getReference().getDigestInputStream();
 +    } else {
 +      return null;
 +    }
 +  }
 +
 +  /**
 +   * Adds the XAdES <code>QualifyingProperties</code> as an
 +   * <code>ds:Object</code> and a corresponding <code>ds:Reference</code> to
 +   * it's <code>SignedProperties</code> element to this Signature.
 +   * 
 +   * @param objects
 +   *          the list of <code>ds:Objects</code> to add the created
 +   *          <code>ds:Object</code> to
 +   * @param references
 +   *          the list of <code>ds:References</code> to add the created
 +   *          <code>ds:Reference</code> to
 +   * 
 +   * @throws SLCommandException
 +   *           if creating and adding the XAdES
 +   *           <code>QualifyingProperties</code> fails
 +   * @throws NullPointerException
 +   *           if <code>objects</code> or <code>references</code> is
 +   *           <code>null</code>
 +   */
 +  private void addXAdESObjectAndReference(List<XMLObject> objects, List<Reference> references) throws SLCommandException {
 +    
 +    QualifyingPropertiesFactory factory = QualifyingPropertiesFactory.getInstance();
 +    
 +    String idValue = ctx.getIdValueFactory().createIdValue("SignedProperties");
 +    
 +    Date date = (signingTime != null) ? signingTime : new Date();
 +    
 +    List<X509Certificate> signingCertificates;
 +    if (signerCertificate != null) {
 +      signingCertificates = Collections.singletonList(signerCertificate);
 +    } else {
 +      signingCertificates = Collections.emptyList();
 +    }
 +    
 +    // TODO: report MOA-SP bug
 +    //
 +    // The security layer specification mandates the use of version 1.2.2. of the
 +    // XAdES QualifyingProperties. However MOA-SP supports only version 1.1.1. Therefore,
 +    // the version 1.1.1 is used in order to be compatible with current MOA-SP versions.
 +    
 +    List<DataObjectFormatType> dataObjectFormats = new ArrayList<DataObjectFormatType>();
 +    for (DataObject dataObject : dataObjects) {
 +      if (dataObject.getMimeType() != null && dataObject.getReference() != null) {
 +        Reference reference = dataObject.getReference();
 +        if (reference.getId() != null) {
 +          String objectReference = "#" + reference.getId();
 +          dataObjectFormats.add(factory.createDataObjectFormatType(
 +              objectReference, dataObject.getMimeType(), dataObject
 +                  .getDescription()));
 +        }
 +      }
 +    }
 +    
 +    JAXBElement<QualifyingPropertiesType> qualifyingProperties;
 +    try {
 +      qualifyingProperties = factory.createQualifyingProperties111(date, signingCertificates, idValue, dataObjectFormats);
 +    } catch (QualifyingPropertiesException e) {
 +      log.error("Failed to create QualifyingProperties.", e);
 +      throw new SLCommandException(4000);
 +    }
 +    
 +    DocumentFragment fragment = ctx.getDocument().createDocumentFragment();
 +    
 +    try {
 +      factory.marshallQualifyingProperties(qualifyingProperties, fragment);
 +    } catch (JAXBException e) {
 +      log.error("Failed to marshal QualifyingProperties.", e);
 +      throw new SLCommandException(4000);
 +    }
 +    
 +    List<DOMStructure> content = Collections.singletonList(new DOMStructure(fragment.getFirstChild()));
 +    
 +    String objectIdValue = ctx.getIdValueFactory().createIdValue("Object");
 +    
 +    XMLObject object = ctx.getSignatureFactory().newXMLObject(content, objectIdValue, null, null);
 +    
 +    objects.add(object);
 +
 +    // TODO: Report MOA-SP Bug
 +    //
 +    // Direct referencing of the SignedPorperties Id-attribute is not supported by MOA-SP
 +    // because the QualifyingProperties are parsed without the XAdES schema. Therefore,
 +    // the shorthand XPointer could not be resolved.
 +    //
 +    // The following workaround uses an XPointer to select the SignedProperties in order
 +    // to allow the signature to be verified with MOA-SP.
 +    
 +    String referenceURI = "#xmlns(xades=http://uri.etsi.org/01903/v1.1.1%23)%20xpointer(id('"
 +        + objectIdValue
 +        + "')/child::xades:QualifyingProperties/child::xades:SignedProperties)";
 +    DigestMethod dm;
 +    try {
 +      dm = ctx.getAlgorithmMethodFactory().createDigestMethod(ctx);
 +    } catch (NoSuchAlgorithmException e) {
 +      log.error("Failed to get DigestMethod algorithm.", e);
 +      throw new SLCommandException(4006);
 +    } catch (InvalidAlgorithmParameterException e) {
 +      log.error("Failed to get DigestMethod algorithm.", e);
 +      throw new SLCommandException(4006);
 +    }
 +    
 +    String referenceIdValue = ctx.getIdValueFactory().createIdValue("Reference");
 +    String referenceType = QualifyingPropertiesFactory.SIGNED_PROPERTIES_REFERENCE_TYPE_V1_1_1;
 +    
 +    Reference reference = ctx.getSignatureFactory().newReference(referenceURI, dm, null, referenceType, referenceIdValue);
 +    
 +    references.add(reference);
 +    
 +    Node child = fragment.getFirstChild();
 +    if (child instanceof Element) {
 +      NodeList nodes = ((Element) child).getElementsByTagNameNS(QualifyingPropertiesFactory.NS_URI_V1_1_1, "SignedProperties");
 +      if (nodes.getLength() > 0) {
 +        IdAttribute idAttribute = new IdAttribute();
 +        idAttribute.element = (Element) nodes.item(0);
 +        idAttribute.namespaceURI = null;
 +        idAttribute.localName = "Id";
 +        idAttributes.add(idAttribute);
 +      }
 +    }
 +    
 +  }
 +
 +  /**
 +   * Parse the SignatureEnvironment.
 +   * 
 +   * @param signatureEnvironment
 +   *          the <code>SignatureEnvironment</code> element
 +   * @param supplements
 +   *          an optional list of <code>Supplements</code> (may be
 +   *          <code>null</code>)
 +   * 
 +   * @return the parsed SignatureEnvironment document
 +   * 
 +   * @throws SLCommandException
 +   *           if parsing the SignatureEnvironment fails
 +   * @throws NullPointerException
 +   *           if <code>signatureEnvironment</code> is <code>null</code>
 +   */
 +  private Document parseSignatureEnvironment(
 +      Base64XMLOptRefContentType signatureEnvironment,
 +      List<DataObjectAssociationType> supplements) throws SLCommandException {
 +
 +    if (signatureEnvironment == null) {
 +      throw new NullPointerException("Argument 'signatureEnvironment' must not be null.");
 +    }
 +    
 +    LSInput input;
 +    try {
 +      if (signatureEnvironment.getReference() != null) {
 +        log.debug("SignatureEnvironment contains Reference " + signatureEnvironment.getReference() + ".");
 +        input = createLSInput(signatureEnvironment.getReference());
 +      } else if (signatureEnvironment.getBase64Content() != null) {
 +        log.debug("SignatureEnvironment contains Base64Content.");
 +        input = createLSInput(signatureEnvironment.getBase64Content());
 +      } else if (signatureEnvironment.getXMLContent() != null) {
 +        log.debug("SignatureEnvironment contains XMLContent.");
 +        input = createLSInput((XMLContentType) signatureEnvironment.getXMLContent());
 +      } else {
 +        // the schema does not allow us to reach this point
 +        throw new SLCommandException(4000);
 +      }
 +    } catch (IOException e) {
 +      log.info("XML document in which the signature is to be integrated cannot be resolved.", e);
 +      throw new SLCommandException(4100);
 +    } catch (XMLStreamException e) {
 +      log.info("XML document in which the signature is to be integrated cannot be resolved.", e);
 +      throw new SLCommandException(4100);
 +    }
 +    
 +    LSParser parser = domImplLS.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS, null);
 +    DOMConfiguration domConfig = parser.getDomConfig();
 +    SimpleDOMErrorHandler errorHandler = new SimpleDOMErrorHandler();
 +    domConfig.setParameter("error-handler", errorHandler);
 +    LSResourceResolverAdapter resourceResolver = new LSResourceResolverAdapter(supplements);
 +    domConfig.setParameter("resource-resolver", resourceResolver);
 +    domConfig.setParameter("validate", Boolean.TRUE);
 +
 +    Document doc;
 +    try {
 +      doc = parser.parse(input);
 +    } catch (DOMException e) {
 +      log.info("XML document in which the signature is to be integrated cannot be parsed.", e);
 +      throw new SLCommandException(4101);
 +    } catch (LSException e) {
 +      log.info("XML document in which the signature is to be integrated cannot be parsed.", e);
 +      throw new SLCommandException(4101);
 +    }
 +    
 +    if (resourceResolver.getError() != null) {
 +      log.info("Failed to resolve resource while parsing SignatureEnvironment document.", resourceResolver.getError());
 +      // we don't stop here, as we only _try_ to parse validating
 +    }
 +    
 +    if (errorHandler.hasFatalErrors()) {
 +      // log fatal errors
 +      if (log.isInfoEnabled()) {
 +        List<String> errorMessages = errorHandler.getErrorMessages();
 +        StringBuffer sb = new StringBuffer();
 +        for (String errorMessage : errorMessages) {
 +          sb.append(" ");
 +          sb.append(errorMessage);
 +        }
 +        log.info("XML document in which the signature is to be integrated cannot be parsed." + sb.toString());
 +      }
 +      throw new SLCommandException(4101);
 +    }
 +
 +    // log parsed document
 +    if (log.isTraceEnabled()) {
 +      
 +      StringWriter writer = new StringWriter();
 +      
 +      writer.write("SignatureEnvironment:\n");
 +      
 +      LSOutput output = domImplLS.createLSOutput();
 +      output.setCharacterStream(writer);
 +      output.setEncoding("UTF-8");
 +      LSSerializer serializer = domImplLS.createLSSerializer();
 +      serializer.write(doc, output);
 +      
 +      log.trace(writer.toString());
 +    }
 +
 +    return doc;
 +    
 +  }
 +
 +  /**
 +   * Creates an LSInput from the given <code>reference</code> URI.
 +   * 
 +   * @param reference
 +   *          the reference URL
 +   * 
 +   * @return an LSInput from the given <code>reference</code> URI
 +   * 
 +   * @throws IOException
 +   *           if dereferencing the given <code>reference</code> fails
 +   */
 +  private LSInput createLSInput(String reference) throws IOException {
 +    
 +    URLDereferencer urlDereferencer = URLDereferencer.getInstance();
 +    StreamData streamData = urlDereferencer.dereference(reference, ctx.getDereferencerContext());
 +
 +    String contentType = streamData.getContentType();
 +    String charset = HttpUtil.getCharset(contentType, true);
 +    InputStreamReader streamReader;
 +    try {
 +      streamReader = new InputStreamReader(streamData.getStream(), charset);
 +    } catch (UnsupportedEncodingException e) {
 +      log.info("Charset " + charset + " not supported. Using default.");
 +      streamReader = new InputStreamReader(streamData.getStream());
 +    }
 +
 +    LSInput input = domImplLS.createLSInput();
 +    input = domImplLS.createLSInput();
 +    input.setCharacterStream(streamReader);
 +    
 +    return input;
 +    
 +  }
 +
 +  /**
 +   * Creates an LSInput from the given <code>content</code> bytes.
 +   * 
 +   * @param content
 +   *          the content bytes
 +   * 
 +   * @return an LSInput from the givne <code>content</code> bytes
 +   */
 +  private LSInput createLSInput(byte[] content) {
 +    
 +    ByteArrayInputStream inputStream = new ByteArrayInputStream(content);
 +    LSInput input = domImplLS.createLSInput();
 +    input.setByteStream(inputStream);
 +
 +    return input;
 +    
 +  }
 +
 +  /**
 +   * Creates an LSInput from the given XML <code>content</code>.
 +   * 
 +   * @param content
 +   *          the XML content
 +   * @return an LSInput from the given XML <code>content</code>
 +   * 
 +   * @throws XMLStreamException
 +   *           if reading the XMLStream from the given XML content fails
 +   */
 +  private LSInput createLSInput(XMLContentType content) throws XMLStreamException {
 +
 +    ByteArrayOutputStream redirectedStream = content.getRedirectedStream();
 +    if (redirectedStream != null) {
 +      LSInput input = domImplLS.createLSInput();
 +      input.setByteStream(new ByteArrayInputStream(redirectedStream.toByteArray()));
 +      return input;
 +    } else {
 +      return null;
 +    }
 +    
 +  }
 +  
 +  /**
 +   * Represents an <code>xsd:Id</code>-attribute value.
 +   * 
 +   * @author mcentner
 +   */
 +  private class IdAttribute {
 +    
 +    private Element element;
 +    
 +    private String namespaceURI;
 +    
 +    private String localName;
 +    
 +  }
 +  
 +  /**
 +   * An implementation of the LSResourceResolver that uses a list of supplements
 +   * to resolve resources.
 +   * 
 +   * @author mcentner
 +   */
 +  private class LSResourceResolverAdapter implements LSResourceResolver {
 +    
 +    List<DataObjectAssociationType> supplements;
 +    
 +    private LSResourceResolverAdapter(
 +        List<DataObjectAssociationType> supplements) {
 +      this.supplements = supplements;
 +    }
 +  
 +    private Exception error;
 +    
 +    /**
 +     * @return the error
 +     */
 +    public Exception getError() {
 +      return error;
 +    }
 +  
 +    @Override
 +    public LSInput resolveResource(String type, String namespaceURI,
 +        String publicId, String systemId, String baseURI) {
 +  
 +      if (log.isTraceEnabled()) {
 +        log.trace("Resolve resource :" +
 +            "\n  type=" + type +
 +            "\n  namespaceURI=" + namespaceURI +
 +            "\n  publicId=" + publicId +
 +            "\n  systemId=" + systemId +
 +            "\n  baseURI=" + baseURI);
 +      }
 +  
 +      if (systemId != null) {
 +
 +        log.debug("Resolve resource '" + systemId + "'.");
 +        
 +        for (DataObjectAssociationType supplement : supplements) {
 +          
 +          Base64XMLLocRefReqRefContentType content = supplement.getContent();
 +          if (content != null) {
 +  
 +            String reference = content.getReference();
 +            if (systemId.equals(reference)) {
 +              
 +              try {
 +                if (content.getLocRefContent() != null) {
 +                  log.trace("Resolved resource '" + reference + "' to supplement with LocRefContent.");
 +                  return createLSInput(content.getLocRefContent());
 +                } else if (content.getBase64Content() != null) {
 +                  log.trace("Resolved resource '" + reference + "' to supplement with Base64Content.");
 +                  return createLSInput(content.getBase64Content());
 +                } else if (content.getXMLContent() != null) {
 +                  log.trace("Resolved resource '" + reference + "' to supplement with XMLContent.");
 +                  return createLSInput((XMLContentType) content.getXMLContent());
 +                } else {
 +                  return null;
 +                }
 +              } catch (IOException e) {
 +                log.info("Failed to resolve resource '" + systemId + "' to supplement.", e);
 +                error = e;
 +                return null;
 +              } catch (XMLStreamException e) {
 +                log.info("Failed to resolve resource '" + systemId + "' to supplement.", e);
 +                error = e;
 +                return null;
 +              }
 +              
 +            }
 +  
 +          }
 +          
 +        }
 +
 +        log.info("Failed to resolve resource '" + systemId + "' to supplement. No such supplement.");
 +        
 +      }
 +  
 +      return null;
 +      
 +    }
 +    
 +    
 +  }
 +
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/SignatureContext.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/SignatureContext.java new file mode 100644 index 00000000..0925f2fd --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/SignatureContext.java @@ -0,0 +1,129 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl.xsect;
 +
 +import javax.xml.crypto.dsig.DigestMethod;
 +import javax.xml.crypto.dsig.XMLSignatureFactory;
 +
 +import org.w3c.dom.Document;
 +
 +import at.gv.egiz.bku.utils.urldereferencer.URLDereferencerContext;
 +
 +/**
 + * An instance of this class carries context information for a XML-Signature
 + * created by the security layer command <code>CreateXMLSignature</code>.
 + * 
 + * @author mcentner
 + */
 +public class SignatureContext {
 +
 +  /**
 +   * The document going to contain the XML signature.
 +   */
 +  private Document document;
 +  
 +  /**
 +   * The IdValueFactory used to create <code>xsd:ID</code>-attribute values.
 +   */
 +  private IdValueFactory idValueFactory;
 +  
 +  /**
 +   * The XMLSignatureFactory to create XML signature objects. 
 +   */
 +  private XMLSignatureFactory signatureFactory;
 +  
 +  /**
 +   * The URLDereferencerContext for dereferencing URLs.
 +   */
 +  private URLDereferencerContext dereferencerContext;
 +  
 +  /**
 +   * The DigestMethodFactory to create {@link DigestMethod} objects.
 +   */
 +  private AlgorithmMethodFactory digestMethodFactory;
 +
 +  /**
 +   * @return the document
 +   */
 +  public Document getDocument() {
 +    return document;
 +  }
 +
 +  /**
 +   * @param document the document to set
 +   */
 +  public void setDocument(Document document) {
 +    this.document = document;
 +  }
 +
 +  /**
 +   * @return the idValueFactory
 +   */
 +  public IdValueFactory getIdValueFactory() {
 +    return idValueFactory;
 +  }
 +
 +  /**
 +   * @param idValueFactory the idValueFactory to set
 +   */
 +  public void setIdValueFactory(IdValueFactory idValueFactory) {
 +    this.idValueFactory = idValueFactory;
 +  }
 +
 +  /**
 +   * @return the signatureFactory
 +   */
 +  public XMLSignatureFactory getSignatureFactory() {
 +    return signatureFactory;
 +  }
 +
 +  /**
 +   * @param signatureFactory the signatureFactory to set
 +   */
 +  public void setSignatureFactory(XMLSignatureFactory signatureFactory) {
 +    this.signatureFactory = signatureFactory;
 +  }
 +
 +  /**
 +   * @return the dereferencerContext
 +   */
 +  public URLDereferencerContext getDereferencerContext() {
 +    return dereferencerContext;
 +  }
 +
 +  /**
 +   * @param dereferencerContext the dereferencerContext to set
 +   */
 +  public void setDereferencerContext(URLDereferencerContext dereferencerContext) {
 +    this.dereferencerContext = dereferencerContext;
 +  }
 +
 +  /**
 +   * @return the digestMethodFactory
 +   */
 +  public AlgorithmMethodFactory getAlgorithmMethodFactory() {
 +    return digestMethodFactory;
 +  }
 +
 +  /**
 +   * @param digestMethodFactory the digestMethodFactory to set
 +   */
 +  public void setAlgorithmMethodFactory(AlgorithmMethodFactory digestMethodFactory) {
 +    this.digestMethodFactory = digestMethodFactory;
 +  }
 +
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/SignatureLocation.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/SignatureLocation.java new file mode 100644 index 00000000..5ec02893 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/SignatureLocation.java @@ -0,0 +1,235 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl.xsect;
 +
 +import java.util.Iterator;
 +
 +import javax.xml.XMLConstants;
 +import javax.xml.namespace.NamespaceContext;
 +import javax.xml.xpath.XPath;
 +import javax.xml.xpath.XPathConstants;
 +import javax.xml.xpath.XPathExpression;
 +import javax.xml.xpath.XPathExpressionException;
 +import javax.xml.xpath.XPathFactory;
 +
 +import org.apache.commons.logging.Log;
 +import org.apache.commons.logging.LogFactory;
 +import org.w3c.dom.Node;
 +import org.w3c.dom.NodeList;
 +
 +import at.buergerkarte.namespaces.securitylayer._1.SignatureInfoCreationType;
 +import at.gv.egiz.bku.slexceptions.SLCommandException;
 +import at.gv.egiz.slbinding.impl.SignatureLocationType;
 +
 +/**
 + * This class implements the <code>SignatureLocation</code> of an XML-Signature
 + * to be created by the security layer command <code>CreateXMLSignature</code>.
 + * 
 + * @author mcentner
 + */
 +public class SignatureLocation {
 +
 +  /**
 +   * Logging facility.
 +   */
 +  private static Log log = LogFactory.getLog(SignatureLocation.class);
 +  
 +  /**
 +   * The SignatureContext for the XML signature
 +   */
 +  private SignatureContext ctx;
 +
 +  /**
 +   * The parent node for the XML signature.
 +   */
 +  private Node parent;
 +  
 +  /**
 +   * The next sibling node for the XML signature.
 +   */
 +  private Node nextSibling;
 +
 +  /**
 +   * Creates a new SignatureLocation with the given <code>signatureContext</code>
 +   * 
 +   * @param signatureContext the context for the XML signature creation
 +   */
 +  public SignatureLocation(SignatureContext signatureContext) {
 +    this.ctx = signatureContext;
 +  }
 +
 +  /**
 +   * @return the parent node for the XML signature
 +   */
 +  public Node getParent() {
 +    return parent;
 +  }
 +
 +  /**
 +   * @param parent the parent for the XML signature
 +   */
 +  public void setParent(Node parent) {
 +    this.parent = parent;
 +  }
 +
 +  /**
 +   * @return the next sibling node for the XML signature
 +   */
 +  public Node getNextSibling() {
 +    return nextSibling;
 +  }
 +
 +  /**
 +   * @param nextSibling the next sibling node for the XML signature
 +   */
 +  public void setNextSibling(Node nextSibling) {
 +    this.nextSibling = nextSibling;
 +  }
 +
 +  /**
 +   * Configures this SignatureLocation with the information provided by the
 +   * given <code>SignatureInfo</code> element.
 +   * 
 +   * @param signatureInfo
 +   *          the <code>SignatureInfo</code> element
 +   * 
 +   * @throws SLCommandException
 +   *           if configuring this SignatureLocation with given
 +   *           <code>signatureInfo</code>fails
 +   */
 +  public void setSignatureInfo(SignatureInfoCreationType signatureInfo)
 +      throws SLCommandException {
 +
 +    // evaluate signature location XPath ...
 +    SignatureLocationType signatureLocation = (SignatureLocationType) signatureInfo
 +        .getSignatureLocation();
 +
 +    NamespaceContext namespaceContext = new MOAIDWorkaroundNamespaceContext(
 +        signatureLocation.getNamespaceContext());
 +
 +    parent = evaluateSignatureLocation(signatureInfo.getSignatureLocation()
 +        .getValue(), namespaceContext, ctx.getDocument().getDocumentElement());
 +
 +    // ... and index
 +    nextSibling = findNextSibling(parent, signatureInfo.getSignatureLocation()
 +        .getIndex().intValue());
 +
 +  }
 +  
 +  /**
 +   * Evaluates the given <code>xpath</code> with the document element as context node
 +   * and returns the resulting node.
 +   * 
 +   * @param xpath the XPath expression
 +   * @param nsContext the namespace context of the XPath expression
 +   * @param contextNode the context node for the XPath evaluation
 +   * 
 +   * @return the result of evaluating the XPath expression
 +   * 
 +   * @throws SLCommandException
 +   */
 +  private Node evaluateSignatureLocation(String xpath, NamespaceContext nsContext, Node contextNode) throws SLCommandException {
 +
 +    Node node = null;
 +    try {
 +      XPathFactory xpathFactory = XPathFactory.newInstance();
 +      XPath xPath = xpathFactory.newXPath();
 +      xPath.setNamespaceContext(nsContext);
 +      XPathExpression xpathExpr = xPath.compile(xpath);
 +      node = (Node) xpathExpr.evaluate(contextNode, XPathConstants.NODE);
 +    } catch (XPathExpressionException e) {
 +      log.info("Failed to evaluate SignatureLocation XPath expression '" + xpath + "' on context node.", e);
 +      throw new SLCommandException(4102);
 +    }
 +
 +    if (node == null) {
 +      log.info("Failed to evaluate SignatureLocation XPath expression '" + xpath + "'. Result is empty.");
 +      throw new SLCommandException(4102);
 +    }
 +    
 +    return node;
 +    
 +  }
 +
 +  /**
 +   * Finds the next sibling node of the <code>parent</code>'s <code>n</code>-th child node
 +   * or <code>null</code> if there is no next sibling.
 +   * 
 +   * @param parent the parent node
 +   * @param n the index of the child node
 +   * 
 +   * @return the next sibling node of the node specified by <code>parent</code> and index <code>n</code>,
 +   * or <code>null</code> if there is no next sibling node.
 +   * 
 +   * @throws SLCommandException if the <code>n</code>-th child of <code>parent</code> does not exist
 +   */
 +  private Node findNextSibling(Node parent, int n) throws SLCommandException {
 +    
 +    NodeList childNodes = parent.getChildNodes();
 +    Node childNode = childNodes.item(n);
 +    if (childNode == null) {
 +      log.info("SingatureLocation Index '" +  n + "' not found in document.");
 +      throw new SLCommandException(4102);
 +    } else {
 +      return childNode.getNextSibling();
 +    }
 +    
 +  }
 +
 +  /**
 +   * Workaround for a missing namespace prefix declaration in MOA-ID.
 +   * 
 +   * @author mcentner
 +   */
 +  private class MOAIDWorkaroundNamespaceContext implements NamespaceContext {
 +
 +    private NamespaceContext namespaceContext;
 +    
 +    public MOAIDWorkaroundNamespaceContext(NamespaceContext namespaceContext) {
 +      super();
 +      this.namespaceContext = namespaceContext;
 +    }
 +
 +    @Override
 +    public String getNamespaceURI(String prefix) {
 +      
 +      String namespaceURI = namespaceContext.getNamespaceURI(prefix);
 +      
 +      if ((namespaceURI == null || XMLConstants.NULL_NS_URI.equals(namespaceURI)) && "saml".equals(prefix)) {
 +        namespaceURI = "urn:oasis:names:tc:SAML:1.0:assertion";
 +        log.debug("Namespace prefix '" + prefix + "' resolved to '" + namespaceURI + "' (MOA-ID Workaround).");
 +      } else {
 +        log.trace("Namespace prefix '" + prefix + "' resolved to '" + namespaceURI + "'.");
 +      }
 +      
 +      return namespaceURI;
 +    }
 +
 +    @Override
 +    public String getPrefix(String namespaceURI) {
 +      return namespaceContext.getPrefix(namespaceURI);
 +    }
 +
 +    @SuppressWarnings("unchecked")
 +    @Override
 +    public Iterator getPrefixes(String namespaceURI) {
 +      return namespaceContext.getPrefixes(namespaceURI);
 +    }
 +    
 +  }
 +  
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/SimpleDOMErrorHandler.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/SimpleDOMErrorHandler.java new file mode 100644 index 00000000..0d54adce --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/SimpleDOMErrorHandler.java @@ -0,0 +1,98 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl.xsect;
 +
 +import java.util.ArrayList;
 +import java.util.List;
 +
 +import org.w3c.dom.DOMError;
 +import org.w3c.dom.DOMErrorHandler;
 +
 +/**
 + * A simple DOMErrorHandler implementation.
 + *  
 + * @author mcentner
 + */
 +public class SimpleDOMErrorHandler implements DOMErrorHandler {
 +
 +  /**
 +   * Have there been errors reported?
 +   */
 +  private boolean errors = false;
 +  
 +  /**
 +   * Have there been fatal error reported?
 +   */
 +  private boolean fatalErrors = false;
 +  
 +  /**
 +   * The list of error messages of reported errors.
 +   */
 +  private List<String> errorMessages = new ArrayList<String>();
 +  
 +  /**
 +   * @return <code>true</code> if errors have been reported, or <code>false</code> otherwise
 +   */
 +  public boolean hasErrors() {
 +    return errors;
 +  }
 +
 +  /**
 +   * @return <code>true</code> if fatal errors have been reported, or <code>false</code> otherwise
 +   */
 +  public boolean hasFatalErrors() {
 +    return fatalErrors;
 +  }
 +
 +  /**
 +   * @return a list of error messages that have been reported
 +   */
 +  public List<String> getErrorMessages() {
 +    return errorMessages;
 +  }
 +
 +  /* (non-Javadoc)
 +   * @see org.w3c.dom.DOMErrorHandler#handleError(org.w3c.dom.DOMError)
 +   */
 +  @Override
 +  public boolean handleError(DOMError error) {
 +
 +    switch (error.getSeverity()) {
 +    
 +      case DOMError.SEVERITY_WARNING : 
 +//        log.debug("[warning] " + error.getMessage());
 +        return true;
 +        
 +      case DOMError.SEVERITY_ERROR : 
 +//        log.debug("[error] " + error.getMessage());
 +        errorMessages.add(error.getMessage());
 +        errors = true;
 +        return false;
 +
 +      case DOMError.SEVERITY_FATAL_ERROR : 
 +//        log.debug("[fatal error] " + error.getMessage());
 +        errorMessages.add(error.getMessage());
 +        fatalErrors = true;
 +        return false;
 +
 +      default:
 +        return false;
 +    }
 +    
 +  }
 +  
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/URIDereferncerAdapter.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/URIDereferncerAdapter.java new file mode 100644 index 00000000..c94937be --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/URIDereferncerAdapter.java @@ -0,0 +1,103 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl.xsect;
 +
 +import iaik.xml.crypto.utils.URIDereferencerImpl;
 +
 +import java.io.IOException;
 +import java.net.URI;
 +import java.net.URISyntaxException;
 +
 +import javax.xml.crypto.Data;
 +import javax.xml.crypto.OctetStreamData;
 +import javax.xml.crypto.URIDereferencer;
 +import javax.xml.crypto.URIReference;
 +import javax.xml.crypto.URIReferenceException;
 +import javax.xml.crypto.XMLCryptoContext;
 +
 +import at.gv.egiz.bku.utils.urldereferencer.StreamData;
 +import at.gv.egiz.bku.utils.urldereferencer.URLDereferencer;
 +import at.gv.egiz.bku.utils.urldereferencer.URLDereferencerContext;
 +
 +/**
 + * An URIDereferencer implementation that uses an {@link URLDereferencer} to
 + * dereference.
 + * 
 + * @author mcentner
 + */
 +public class URIDereferncerAdapter implements URIDereferencer {
 +
 +  /**
 +   * The context for dereferencing.
 +   */
 +  protected URLDereferencerContext urlDereferencerContext;
 +
 +  /**
 +   * Creates a new URIDereferencerAdapter instance with the given
 +   * <code>urlDereferencerContext</code>.
 +   * 
 +   * @param urlDereferencerContext the context to be used for dereferencing
 +   */
 +  public URIDereferncerAdapter(URLDereferencerContext urlDereferencerContext) {
 +    super();
 +    this.urlDereferencerContext = urlDereferencerContext;
 +  }
 +
 +  /* (non-Javadoc)
 +   * @see javax.xml.crypto.URIDereferencer#dereference(javax.xml.crypto.URIReference, javax.xml.crypto.XMLCryptoContext)
 +   */
 +  @Override
 +  public Data dereference(URIReference uriReference, XMLCryptoContext context)
 +      throws URIReferenceException {
 +    
 +    String uriString = uriReference.getURI();
 +    if (uriString == null) {
 +      return null;
 +    }
 +    
 +    URI uri;
 +    try {
 +      uri = new URI(uriString);
 +    } catch (URISyntaxException e) {
 +      throw new URIReferenceException(e.getMessage(), e);
 +    }
 +    
 +    if (uri.isAbsolute()) {
 +
 +      URLDereferencer dereferencer = URLDereferencer.getInstance();
 +      StreamData streamData;
 +      try {
 +        streamData = dereferencer.dereference(uriString, urlDereferencerContext);
 +      } catch (IOException e) {
 +        throw new URIReferenceException(e.getMessage(), e);
 +      }
 +      return new OctetStreamData(streamData.getStream(), uriString, streamData.getContentType());
 +      
 +    } else {
 +      
 +      URIDereferencer uriDereferencer = context.getURIDereferencer();
 +      if (uriDereferencer == null || uriDereferencer == this) {
 +        uriDereferencer = new URIDereferencerImpl();
 +      }
 +        
 +      return uriDereferencer.dereference(uriReference, context);
 +      
 +    }
 +    
 +  }
 +
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/XSECTReference.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/XSECTReference.java new file mode 100644 index 00000000..6b388f2a --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/XSECTReference.java @@ -0,0 +1,112 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl.xsect;
 +
 +import iaik.xml.crypto.dsig.DigestMethodImpl;
 +import iaik.xml.crypto.dsig.DigestValueImpl;
 +import iaik.xml.crypto.dsig.ReferenceImpl;
 +import iaik.xml.crypto.dsig.TransformImpl;
 +import iaik.xml.crypto.dsig.TransformsImpl;
 +
 +import javax.xml.crypto.Data;
 +import javax.xml.crypto.URIDereferencer;
 +import javax.xml.crypto.URIReferenceException;
 +import javax.xml.crypto.XMLCryptoContext;
 +import javax.xml.crypto.dsig.DigestMethod;
 +import javax.xml.crypto.dsig.TransformException;
 +
 +import at.gv.egiz.bku.slexceptions.SLCommandException;
 +import at.gv.egiz.bku.slexceptions.SLExceptionMessages;
 +
 +/**
 + * This class extends the XSECT ReferenceImpl to allow for the use
 + * of already marshalled <code>ds:Transforms</code> elements for initialization.
 + *  
 + * @author mcentner
 + */
 +public class XSECTReference extends ReferenceImpl {
 +
 +  /**
 +   * The URIDereferencer to be used for dereferencing.
 +   */
 +  protected URIDereferencer dereferencer;
 +
 +  /**
 +   * Creates a new instance of this XSECTReference with the given
 +   * <code>uri</code>, digest method, <code>transforms</code>, <code>type</code>
 +   * and <code>id</code> value.
 +   * 
 +   * @param uri
 +   *          the <code>URI</code>-attribute value (may be <code>null</code>)
 +   * @param dm
 +   *          the digest method
 +   * @param transforms
 +   *          a TransformsImpl element (may be <code>null</code>)
 +   * @param type
 +   *          the <code>Type</code>-attribute value (may be <code>null</code>)
 +   * @param id
 +   *          the <code>Id</code>-attribute value (may be <code>null</code>)
 +   * 
 +   * @throws NullPointerException
 +   *           if <code>digestMethod</code> is <code>null</code>
 +   * @throws IllegalArgumentException
 +   *           if <code>uri</code> is not RFC 2396 compliant
 +   * @throws ClassCastException
 +   *           if any of the <code>transforms</code> is not of type
 +   *           {@link TransformImpl}
 +   */
 +  public XSECTReference(String uri, DigestMethod dm, TransformsImpl transforms, String type,
 +      String id) {
 +    super(uri, transforms, type, id);
 +    digestMethod_ = (DigestMethodImpl) dm;
 +    digestValue_ = new DigestValueImpl();
 +  }
 +
 +  /* (non-Javadoc)
 +   * @see iaik.xml.crypto.dsig.ReferenceType#dereference(javax.xml.crypto.XMLCryptoContext)
 +   */
 +  @Override
 +  public Data dereference(XMLCryptoContext context) throws TransformException,
 +      URIReferenceException {
 +    if (dereferencer != null) {
 +      return dereferencer.dereference(this, context);
 +    } else {
 +      try {
 +        return super.dereference(context);
 +      } catch (URIReferenceException e) {
 +        SLCommandException commandException = new SLCommandException(4003,
 +            SLExceptionMessages.EC4003_NOT_RESOLVED, new Object[] { getURI() });
 +        throw new URIReferenceException("Failed to dereference data to-be signed.", commandException);
 +      }
 +    }
 +  }
 +
 +  /**
 +   * @return the dereferencer to be used for dereferencing this reference
 +   */
 +  public URIDereferencer getDereferencer() {
 +    return dereferencer;
 +  }
 +
 +  /**
 +   * @param dereferencer the dereferencer to be used for dereferencing this reference
 +   */
 +  public void setDereferencer(URIDereferencer dereferencer) {
 +    this.dereferencer = dereferencer;
 +  }
 +
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/XSECTTransforms.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/XSECTTransforms.java new file mode 100644 index 00000000..a98e4236 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/XSECTTransforms.java @@ -0,0 +1,124 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slcommands.impl.xsect;
 +
 +import iaik.xml.crypto.dsig.TransformImpl;
 +import iaik.xml.crypto.dsig.TransformsImpl;
 +
 +import java.util.List;
 +
 +import javax.xml.crypto.MarshalException;
 +import javax.xml.crypto.dom.DOMCryptoContext;
 +import javax.xml.crypto.dsig.Transform;
 +
 +import org.w3c.dom.Node;
 +
 +/**
 + * This class extends the XSECT TransformsImpl to allow for the use of an
 + * unmarshalled <code>ds:Transforms</code> element for initalization.
 + * 
 + * @author mcentner
 + */
 +public class XSECTTransforms extends TransformsImpl {
 + 
 +  /**
 +   * Creates a new XSECTTransforms with the given list of <code>transforms</code>.
 +   * 
 +   * @param transforms a list of {@link TransformImpl}s
 +   * @see TransformsImpl#TransformsImpl(List)
 +   */
 +  @SuppressWarnings("unchecked")
 +  public XSECTTransforms(List transforms) {
 +    super(transforms);
 +  }
 +
 +  /**
 +   * Creates a new XSECTTransforms and initializes it from the given
 +   * <code>ds:Transforms</code> node.
 +   * 
 +   * @param context the context used for unmarshalling
 +   * @param node the <code>ds:Transforms</code> node
 +   * 
 +   * @throws MarshalException if unmarshalling the <code>ds:Transforms</code> fails
 +   */
 +  public XSECTTransforms(DOMCryptoContext context, Node node)
 +      throws MarshalException {
 +    super(context, node);
 +  }
 +
 +  /**
 +   * Inserts the given <code>transform</code> at the top of the
 +   * transform list.
 +   * 
 +   * @param transform the <code>ds:Transform</code> to instert
 +   */
 +  @SuppressWarnings("unchecked")
 +  public void insertTransform(Transform transform) {
 +    if (transform == null) {
 +      throw new NullPointerException("Parameter 'transform' must not be null.");
 +    }
 +    if (!(transform instanceof TransformImpl)) {
 +      throw new ClassCastException("Transform 'transform' must be of type '" + TransformImpl.class.getName() + "'.");
 +    }
 +    transforms_.add(0, transform);
 +  }
 +  
 +  /**
 +   * @return
 +   */
 +  @SuppressWarnings("unchecked")
 +  private List<TransformImpl> getTransformImpls() {
 +    return transforms_;
 +  }
 +
 +  /* (non-Javadoc)
 +   * @see iaik.xml.crypto.dsig.TransformsType#marshal(javax.xml.crypto.dom.DOMCryptoContext, org.w3c.dom.Node, org.w3c.dom.Node)
 +   */
 +  @Override
 +  public Node marshal(DOMCryptoContext context, Node parent, Node nextSibling)
 +      throws MarshalException {
 +
 +    if (getNode() != null) {
 +      // If this TransformsImpl has been unmarshalled from exiting nodes,
 +      // we don't want to re-marshal ...
 +      state_ = STATE_MARSHALED;
 +      
 +      // ... but append the existing node to the parent ...
 +      Node transformsNode = parent.insertBefore(getNode(), nextSibling);
 +      
 +      // ... and marshal any Transforms not yet marshalled (e.g. that
 +      // have been added via insertTransform().
 +      Node transformNextSibling = transformsNode.getFirstChild();
 +      List<TransformImpl> transforms = getTransformImpls();
 +      for (int i = 0; i < transforms.size(); i++) {
 +        TransformImpl transform = transforms.get(i);
 +        Node transformNode = transform.getNode();
 +        if (transformNode == null) {
 +          // marshall TransformImpl
 +          transformNode = transform.marshal(context, transformsNode, transformNextSibling);
 +        }
 +        transformNextSibling = transformNode.getNextSibling();
 +      }
 +      
 +      return transformsNode;
 +    } else {
 +      return super.marshal(context, parent, nextSibling);
 +    }
 +    
 +  }
 +  
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLBindingException.java b/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLBindingException.java new file mode 100644 index 00000000..3f1732ba --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLBindingException.java @@ -0,0 +1,31 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slexceptions;
 +
 +/**
 + * Error in the binding to the transport protocol (2xxx)
 + */
 +public class SLBindingException extends SLException {
 +
 +  public SLBindingException(int errorCode) {
 +    super(errorCode);
 +  }
 +
 +  public SLBindingException(int errorCode, String msg, Object[] args) {
 +    super(errorCode, msg, args);
 +  }
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLCanceledException.java b/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLCanceledException.java new file mode 100644 index 00000000..8136a093 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLCanceledException.java @@ -0,0 +1,26 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slexceptions;
 +
 +public class SLCanceledException extends
 +    at.gv.egiz.bku.slexceptions.SLException {
 +
 +  public SLCanceledException(int errorCode, String msg, Object[] args) {
 +    super(errorCode, msg, args);
 +    // TODO Auto-generated constructor stub
 +  }
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLCommandException.java b/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLCommandException.java new file mode 100644 index 00000000..73ae3325 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLCommandException.java @@ -0,0 +1,30 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slexceptions;
 +
 +public class SLCommandException extends at.gv.egiz.bku.slexceptions.SLException {
 +
 +  private static final long serialVersionUID = 1L;
 +
 +  public SLCommandException(int errorCode) {
 +    super(errorCode);
 +  }
 +  
 +  public SLCommandException(int errorCode, String msg, Object[] args) {
 +    super(errorCode, msg, args);
 +  }
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLException.java b/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLException.java new file mode 100644 index 00000000..4b541deb --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLException.java @@ -0,0 +1,88 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slexceptions;
 +
 +import java.text.MessageFormat;
 +import java.util.Locale;
 +import java.util.MissingResourceException;
 +import java.util.ResourceBundle;
 +
 +public class SLException extends Exception {
 +
 +  private static String RESOURCE_BUNDLE_BASE_NAME = "at.gv.egiz.bku.slexceptions.SLExceptionMessages";
 +  
 +  private static String MISSING_RESOURCE_PATTERN = "MISSING RESOURCE FOR ERROR MESSAGE: {0} ({1})";
 +  
 +  private static String ILLEGAL_ARGUMENT_MESSAGE = "MESSAGE FORMAT FAILED";
 +  
 +  private static final long serialVersionUID = 1L;
 +
 +  private int errorCode;
 +  
 +  private String message;
 +  
 +  private Object[] arguments;
 +  
 +  public SLException(int errorCode) {
 +    this.errorCode = errorCode;
 +    this.message = SLExceptionMessages.STANDARD_PREFIX + Integer.toString(errorCode);
 +  }
 +
 +  public SLException(int errorCode, String message, Object[] arguments) {
 +    this.errorCode = errorCode;
 +    this.message = message;
 +    this.arguments = arguments;
 +  }
 +
 +  public int getErrorCode() {
 +    return errorCode;
 +  }
 +
 +  public String getDetailedMsg() {
 +    return getLocalizedMessage();
 +  }
 +
 +  @Override
 +  public String getLocalizedMessage() {
 +    return getLocalizedMessage(Locale.getDefault());
 +  }
 +
 +  public String getLocalizedMessage(Locale locale) {
 +    
 +    String pattern;
 +    Object[] arguments = this.arguments;
 +    try {
 +      ResourceBundle bundle = ResourceBundle.getBundle(RESOURCE_BUNDLE_BASE_NAME, locale);
 +      pattern = bundle.getString(message);
 +    } catch (MissingResourceException e) {
 +      pattern = MISSING_RESOURCE_PATTERN;
 +      arguments = new Object[]{message, e.getMessage()};
 +    }
 +    
 +    String localizedMessage;
 +    try {
 +      localizedMessage = MessageFormat.format(pattern, arguments);
 +    } catch (IllegalArgumentException e) {
 +      localizedMessage = ILLEGAL_ARGUMENT_MESSAGE + ": " + pattern;
 +    }
 +    
 +    return localizedMessage;
 +    
 +  }
 +  
 +
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLExceptionMessages.java b/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLExceptionMessages.java new file mode 100644 index 00000000..5ce5cba1 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLExceptionMessages.java @@ -0,0 +1,50 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slexceptions; + +public final class SLExceptionMessages { + +  private SLExceptionMessages() { +  } + +  public static final String STANDARD_PREFIX = "ec"; +   +  // +  // 3xxx +  // +  // Error in the XML structure of the command request +   +  public static final String EC3000_UNCLASSIFIED = "ec3000.unclassified"; +   +  public static final String EC3002_INVALID = "ec3002.invalid"; + +  // +  // 4xxx +  // +  // Error during command execution +   +  public static final String EC4000_UNCLASSIFIED_INFOBOX_INVALID = "ec4000.infobox.invalid"; +   +  public static final String EC4000_UNCLASSIFIED_IDLINK_TRANSFORMATION_FAILED = "ec4000.idlink.transfomation.failed"; +   +  public static final String EC4002_INFOBOX_UNKNOWN = "ec4002.infobox.unknown"; +   +  public static final String EC4003_NOT_RESOLVED = "ec4003.not.resolved"; +   +  public static final String EC4011_NOTIMPLEMENTED = "ec4011.notimplemented"; +   +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLRequestException.java b/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLRequestException.java new file mode 100644 index 00000000..548732e6 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLRequestException.java @@ -0,0 +1,30 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slexceptions;
 +
 +public class SLRequestException extends SLException {
 +
 +  public SLRequestException(int errorCode) {
 +    super(errorCode);
 +    // TODO Auto-generated constructor stub
 +  }
 +
 +  public SLRequestException(int errorCode, String msg, Object[] args) {
 +    super(errorCode, msg, args);
 +    // TODO Auto-generated constructor stub
 +  }
 +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLRuntimeException.java b/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLRuntimeException.java new file mode 100644 index 00000000..d09ca418 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLRuntimeException.java @@ -0,0 +1,37 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slexceptions;
 +
 +public class SLRuntimeException extends RuntimeException {
 +
 +  public SLRuntimeException(String message, Throwable cause) {
 +    super(message, cause);
 +  }
 +
 +  public SLRuntimeException(String message) {
 +    super(message);
 +  }
 +
 +  public SLRuntimeException(Throwable cause) {
 +    super(cause);
 +  }
 +
 +  public SLRuntimeException() {
 +  }
 +  
 +  
 +}
 diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLViewerException.java b/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLViewerException.java new file mode 100644 index 00000000..1d128a00 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLViewerException.java @@ -0,0 +1,25 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* 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 at.gv.egiz.bku.slexceptions;
 +
 +public class SLViewerException extends SLException {
 +
 +  public SLViewerException(int errorCode, String msg, Object[] args) {
 +    super(errorCode, msg, args);
 +    // TODO Auto-generated constructor stub
 +  }
 +}
\ No newline at end of file | 
