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

se.swedenconnect.cert.extensions.data.saci.AbstractDomData Maven / Gradle / Ivy

/*
 * Copyright (c) 2023.  Sweden Connect
 *
 * 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.swedenconnect.cert.extensions.data.saci;

import java.security.cert.CertificateException;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import lombok.NoArgsConstructor;

/**
 * Abstract class providing basics for xml elements that are part of the SAMLAuthContext implementation
 */
@NoArgsConstructor
public abstract class AbstractDomData {

  /** Formatter for XML date element content */
  public static final DateTimeFormatter XML_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(
    "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ", Locale.ENGLISH).withZone(ZoneId.systemDefault());

  /** XML name space URI for the RFC 7773 SAMLAuthContext XML element */
  public static final String SACI_NS = "http://id.elegnamnden.se/auth-cont/1.0/saci";
  /** XML name space URI for SAML assertion data */
  public static final String SAML_ASSERTION_NS = "urn:oasis:names:tc:SAML:2.0:assertion";

  /** indicates if content validation should be done with strict compliance requirements */
  protected boolean strictMode;

  /**
   * Constructor for the base Abstract class
   *
   * @param element The element holding a SAMLAuthContextExtension extension
   *
   * @throws CertificateException content validation error
   */
  public AbstractDomData(final Element element) throws CertificateException {
    this(element, false);
  }

  /**
   * Constructor for the base Abstract class setting strict mode
   *
   * @param element The element holding a SAMLAuthContextExtension extension
   * @param strictMode true to strictly enforce content requirement rules
   * @throws CertificateException content validation error
   */
  public AbstractDomData(final Element element, final boolean strictMode) throws CertificateException {
    this.strictMode = strictMode;
    setValuesFromElement(element);
    validate();
  }

  /**
   * Creates an instance of this elements data from a DOM element
   *
   * @param element xml element providing content data
   * @throws CertificateException content validation error
   */
  protected abstract void setValuesFromElement(final Element element) throws CertificateException;

  /**
   * Gets a DOM element from the element data fields
   *
   * @param document the document this element shall belong to
   * @return DOM element populated with the field data of this object
   */
  protected abstract Element getElement(final Document document);

  /**
   * Validates the data fields of this object to assert that it meets basic content requirements
   *
   * @throws CertificateException validation errors
   */
  protected abstract void validate() throws CertificateException;

  /**
   * Get the first available element from a parent element matching specified name parameters
   *
   * @param parent the parent element
   * @param namespaceUri name space URI for the requested element
   * @param elementName the name of the requested element
   * @return the first available element matching the name criteria or null if no such element exists in the parent element
   */
  public static Element getSingleElement(final Element parent, final String namespaceUri, final String elementName) {
    final List elementList = getElements(parent, namespaceUri, elementName);
    if (elementList.isEmpty()) {
      return null;
    }
    return elementList.get(0);
  }

  /**
   * Get elements from parent element that meets name requirements
   *
   * @param parent the parent element
   * @param namespaceUri name space URI for the requested element
   * @param elementName the name of the requested elements
   * @return all available elements matching the name criteria or null if no such element exists in the parent element
   */
  public static List getElements(final Element parent, final String namespaceUri, final String elementName) {
    final NodeList elements = parent.getElementsByTagNameNS(namespaceUri, elementName);
    List elementList = new ArrayList<>();
    for (int i = 0; i < elements.getLength(); i++) {
      final Node node = elements.item(i);
      if (node instanceof Element) {
        elementList.add((Element) node);
      }
    }
    return elementList;
  }

  /**
   * Sets an XML element attribute
   *
   * @param element the element where the attribute should be added
   * @param name name of the attribute
   * @param value value of the attribute
   */
  public static void setAttribute(Element element, String name, String value) {
    setAttribute(element, name, value, null);
  }

  /**
   * Sets an XML element attribute with namespace URI declaration
   *
   * @param element the element where the attribute should be added
   * @param name name of the attribute
   * @param value value of the attribute
   * @param namespaceUri name space URI of the attribute
   */
  public static void setAttribute(Element element, String name, String value, String namespaceUri) {
    if (value == null) {
      return;
    }
    if (namespaceUri == null) {
      element.setAttribute(name, value);
      return;
    }
    element.setAttributeNS(namespaceUri, name, value);
  }

  /**
   * Get the attribute value of an element attribute
   *
   * @param element the element holding the attribute value
   * @param name name of the attribute
   * @return attribute value or null if no such attribute is present with a non-empty value
   */
  public static String getAttributeValue(Element element, String name) {
    return getAttributeValue(element, name, null);
  }

  /**
   * Get the attribute value of an element attribute supporting a defined name space URI
   *
   * @param element the element holding the attribute value
   * @param name name of the attribute
   * @param namespaceUri name space URI of the attribute
   * @return attribute value or null if no such attribute is present with a non-empty value
   */
  public static String getAttributeValue(Element element, String name, String namespaceUri) {
    String val = namespaceUri == null
      ? element.getAttribute(name)
      : element.getAttributeNS(namespaceUri, name);

    return (val.length() > 0) ? val : null;
  }

  /**
   * Get all attributes of an element that is not listed in an excludeList.
   * This is used to locate extension attributes with any declaration that are not part of the defined set.
   *
   * @param element element holding attributes
   * @param excludeList list of attribute names that should be excluded from the result list
   * @return list of attributes not present in the exclude list
   */
  public static List getOtherAttributes(Element element, List excludeList) {

    List attrList = new ArrayList<>();
    NamedNodeMap attributes = element.getAttributes();
    for (int i = 0; i < attributes.getLength(); i++) {
      Node item = attributes.item(i);
      if (item instanceof Attr) {
        Attr attrItem = (Attr) item;
        if (!excludeList.contains(attrItem.getName())) {
          attrList.add(attrItem);
        }
      }
    }
    return attrList;
  }

  /**
   * Adopting a list of elements in a document and adding them to a specific element
   *
   * @param adoptingElement the element where the adopted elements should be added
   * @param document document adopting the elements
   * @param toBeAdoptedList list of elements to be adopted and added to the adopting element
   */
  public static void adoptElements(Element adoptingElement, Document document, List toBeAdoptedList) {
    if (toBeAdoptedList == null) {
      return;
    }
    toBeAdoptedList.forEach(element -> {
      document.adoptNode(element);
      adoptingElement.appendChild(element);
    });
  }

  /**
   * Adopting a list of attribute nodes in an element
   *
   * @param adoptingElement the element where the provided attributes should be added
   * @param document document adopting the attribute nodes
   * @param toBeAdoptedList list of attributes to be adopted and added
   */
  public static void adoptAttributes(Element adoptingElement, Document document, List toBeAdoptedList) {
    if (toBeAdoptedList == null) {
      return;
    }
    toBeAdoptedList.forEach(attr -> {
      document.adoptNode(attr);
      adoptingElement.setAttributeNode(attr);
    });
  }

  /**
   * Converts an Instant time to an XML formatted xs:dateTime value
   *
   * @param instant time to be converted
   * @return XML string formatted as xs:dateTime
   */
  public static String instantToString(Instant instant) {
    if (instant == null) {
      return null;
    }
    return XML_DATE_TIME_FORMATTER.format(instant);
  }

  /**
   * Converts an XML xs:dateTime string to Instant
   *
   * @param xmlTimeStr string expression of an xs:dateTime value
   * @return Instant object
   */
  public static Instant parseTime(String xmlTimeStr) {
    if (xmlTimeStr == null) {
      return null;
    }
    return Instant.from(OffsetDateTime.parse(xmlTimeStr));
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy