All Downloads are FREE. Search and download functionalities are using the official Maven repository.

se.idsec.signservice.integration.document.impl.AbstractTbsDocumentProcessor Maven / Gradle / Ivy

/*
 * Copyright 2019-2023 IDsec Solutions AB
 *
 * 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 se.idsec.signservice.integration.document.impl;

import jakarta.annotation.Nonnull;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import se.idsec.signservice.integration.SignRequestInput;
import se.idsec.signservice.integration.config.IntegrationServiceConfiguration;
import se.idsec.signservice.integration.core.DocumentCache;
import se.idsec.signservice.integration.core.error.InputValidationException;
import se.idsec.signservice.integration.core.error.NoAccessException;
import se.idsec.signservice.integration.core.impl.CorrelationID;
import se.idsec.signservice.integration.document.DocumentDecoder;
import se.idsec.signservice.integration.document.DocumentProcessingException;
import se.idsec.signservice.integration.document.ProcessedTbsDocument;
import se.idsec.signservice.integration.document.TbsDocument;
import se.idsec.signservice.integration.document.TbsDocumentProcessor;
import se.idsec.signservice.utils.AssertThat;
import se.swedenconnect.schemas.csig.dssext_1_1.AdESObject;
import se.swedenconnect.schemas.csig.dssext_1_1.SignTaskData;

import java.util.UUID;

/**
 * Abstract base class for {@link TbsDocumentProcessor} implementations.
 *
 * @param  document type
 * @author Martin Lindström ([email protected])
 * @author Stefan Santesson ([email protected])
 */
@Slf4j
public abstract class AbstractTbsDocumentProcessor implements TbsDocumentProcessor {

  /** Validator. */
  private TbsDocumentValidator tbsDocumentValidator;

  /** Object factory for DSS-Ext objects. */
  private static final se.swedenconnect.schemas.csig.dssext_1_1.ObjectFactory dssExtObjectFactory =
      new se.swedenconnect.schemas.csig.dssext_1_1.ObjectFactory();

  /** {@inheritDoc} */
  @Override
  public ProcessedTbsDocument preProcess(final TbsDocument document, final SignRequestInput signRequestInput,
      final IntegrationServiceConfiguration config, final DocumentCache documentCache, final String callerId,
      final String fieldName) throws InputValidationException {

    // Make a copy of the document before updating it.
    final TbsDocument updatedDocument = document.toBuilder().build();

    if (document.getId() == null) {
      updatedDocument.setId(UUID.randomUUID().toString());
      log.info("{}: No document ID assigned to document, assigning generated id: {}", CorrelationID.id(),
          updatedDocument.getId());
    }

    if (document.getAdesRequirement() != null && document.getAdesRequirement().getAdesFormat() == null) {
      log.warn("{}: No AdES format assigned for AdES requirement for document '{}'", CorrelationID.id(),
          updatedDocument.getId());
      updatedDocument.setAdesRequirement(null);
    }

    // Validate
    //
    this.getTbsDocumentValidator().validateObject(updatedDocument, fieldName, config);

    // Check if the document holds a content reference, if so, get the cached document ...
    //
    if (StringUtils.isNotBlank(updatedDocument.getContentReference())) {
      if (documentCache == null) {
        throw new RuntimeException("No document cache available");
      }
      try {
        final String cachedDocument = documentCache.get(updatedDocument.getContentReference(), true, callerId);

        if (cachedDocument == null) {
          final String msg = String.format("reference '%s' not found in cache", updatedDocument.getContentReference());
          log.error("{}: {}", CorrelationID.id(), msg);
          throw new InputValidationException(fieldName + ".contentReference", msg);
        }
        // Replace the content reference with the actual contents ...
        updatedDocument.setContent(cachedDocument);
        updatedDocument.setContentReference(null);
      }
      catch (final NoAccessException e) {
        final String msg = String.format("Caller does not have access to referenced document '%s'",
            updatedDocument.getContentReference());
        log.error("{}: {}", CorrelationID.id(), msg);
        throw new InputValidationException(fieldName + ".contentReference", msg, e);
      }
    }

    // Validate the document content ...
    //
    final T validatedContent = this.validateDocumentContent(updatedDocument, config, fieldName);

    return new ProcessedTbsDocument(updatedDocument, validatedContent);
  }

  /** {@inheritDoc} */
  @Override
  public final SignTaskData process(@Nonnull final ProcessedTbsDocument document,
      @Nonnull final String signatureAlgorithm,
      @Nonnull final IntegrationServiceConfiguration config) throws DocumentProcessingException {

    final TbsCalculationResult tbsCalculation = this.calculateToBeSigned(document, signatureAlgorithm, config);
    final TbsDocument tbsDocument = document.getTbsDocument();

    final SignTaskData signTaskData = dssExtObjectFactory.createSignTaskData();
    signTaskData.setSignTaskId(tbsDocument.getId());
    signTaskData.setSigType(tbsCalculation.getSigType());
    signTaskData.setToBeSignedBytes(tbsCalculation.getToBeSignedBytes());
    if (tbsDocument.getAdesRequirement() != null) {
      signTaskData.setAdESType(tbsDocument.getAdesRequirement().getAdesFormat().name());
      if (tbsCalculation.getAdesSignatureId() != null) {
        final AdESObject adesObject = dssExtObjectFactory.createAdESObject();
        adesObject.setSignatureId(tbsCalculation.getAdesSignatureId());
        if (tbsCalculation.getAdesObjectBytes() != null) {
          adesObject.setAdESObjectBytes(tbsCalculation.getAdesObjectBytes());
        }
        signTaskData.setAdESObject(adesObject);
      }
      // TODO: Is this correct?
      if (StringUtils.isNotBlank(tbsDocument.getAdesRequirement().getSignaturePolicy())) {
        signTaskData.setProcessingRules(tbsDocument.getAdesRequirement().getSignaturePolicy());
      }
    }
    else {
      signTaskData.setAdESType("None");
    }

    return signTaskData;
  }

  /**
   * Calculates the ToBeSignedBytes, and optionally AdES data, that will be part of the {@code SignTaskData}.
   *
   * @param document the document to sign
   * @param signatureAlgorithm the signature algorithm to be used for signing the document
   * @param config the profile configuration
   * @return the TBS bytes and optionally AdES data
   * @throws DocumentProcessingException for processing errors
   */
  protected abstract TbsCalculationResult calculateToBeSigned(final ProcessedTbsDocument document,
      final String signatureAlgorithm,
      final IntegrationServiceConfiguration config) throws DocumentProcessingException;

  /**
   * Gets the validator for checking AdES requirements.
   *
   * @return validator for AdES requirements
   */
  protected abstract EtsiAdesRequirementValidator getEtsiAdesRequirementValidator();

  /**
   * Validates the document contents. The default implementation invokes
   * {@link DocumentDecoder#decodeDocument(String)}.
   *
   * @param document the document holding the content to validate
   * @param config the current policy configuration
   * @param fieldName used for error reporting and logging
   * @return the contents represented according to the document format
   * @throws InputValidationException for validation errors
   */
  protected T validateDocumentContent(
      final TbsDocument document, final IntegrationServiceConfiguration config, final String fieldName)
      throws InputValidationException {

    try {
      final T documentObject = this.getDocumentDecoder().decodeDocument(document.getContent());
      log.debug("{}: Successfully validated document (doc-id: {})", CorrelationID.id(), document.getId());
      return documentObject;
    }
    catch (final DocumentProcessingException e) {
      final String msg =
          String.format("Failed to load content for document '%s' - %s", document.getId(), e.getMessage());
      log.error("{}: {}", CorrelationID.id(), msg, e);
      throw new InputValidationException(fieldName + ".content", msg, e);
    }
  }

  /**
   * Gets the {@link TbsDocumentValidator} to use while processing.
   *
   * @return a TbsDocumentValidator
   */
  protected TbsDocumentValidator getTbsDocumentValidator() {
    if (this.tbsDocumentValidator == null) {
      this.tbsDocumentValidator = new TbsDocumentValidator(this.getEtsiAdesRequirementValidator());
    }
    return this.tbsDocumentValidator;
  }

  /**
   * Ensures that the {@link #getEtsiAdesRequirementValidator()} does not return {@code null} and sets up a
   * {@link TbsDocumentValidator}.
   *
   * 

* Note: If executing in a Spring Framework environment this method is automatically invoked after all properties have * been assigned. Otherwise it should be explicitly invoked. *

* * @throws Exception for init errors */ @PostConstruct public void afterPropertiesSet() throws Exception { AssertThat.isNotNull(this.getEtsiAdesRequirementValidator(), "etsiAdesRequirementValidator must not be null"); this.tbsDocumentValidator = new TbsDocumentValidator(this.getEtsiAdesRequirementValidator()); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy