AuthenticationFileBasedConfiguration.java

/*
 * This file is part of Waarp Project (named also Waarp or GG).
 *
 *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
 *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
 * individual contributors.
 *
 *  All Waarp Project is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along with
 * Waarp . If not, see <http://www.gnu.org/licenses/>.
 */
package org.waarp.openr66.configuration;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.tree.DefaultElement;
import org.waarp.common.database.exception.WaarpDatabaseException;
import org.waarp.common.database.exception.WaarpDatabaseNoConnectionException;
import org.waarp.common.database.exception.WaarpDatabaseSqlException;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import org.waarp.common.xml.XmlHash;
import org.waarp.common.xml.XmlUtil;
import org.waarp.common.xml.XmlValue;
import org.waarp.openr66.database.data.DbHostAuth;
import org.waarp.openr66.protocol.configuration.Configuration;
import org.waarp.openr66.protocol.exception.OpenR66ProtocolSystemException;

import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;

import static org.waarp.openr66.configuration.FileBasedElements.*;

/**
 * Authentication from File support
 */
public class AuthenticationFileBasedConfiguration {
  private static final String CANNOT_READ_KEY_FOR_HOST_ID =
      "Cannot read key for hostId ";
  /**
   * Internal Logger
   */
  private static final WaarpLogger logger =
      WaarpLoggerFactory.getLogger(AuthenticationFileBasedConfiguration.class);

  private AuthenticationFileBasedConfiguration() {
  }

  /**
   * Load Authentication from File
   *
   * @param filename
   *
   * @return True if OK
   */
  @SuppressWarnings("unchecked")
  public static boolean loadAuthentication(final Configuration config,
                                           final String filename) {
    final Document document;
    try {
      document = XmlUtil.getNewSaxReader().read(filename);
    } catch (final DocumentException e) {
      logger.error("Unable to read the XML Authentication file: " + filename,
                   e);
      return false;
    }
    if (document == null) {
      logger.error("Unable to read the XML Authentication file: " + filename);
      return false;
    }
    final XmlValue[] values = XmlUtil.read(document, authentElements);
    final XmlHash hash = new XmlHash(values);
    XmlValue value = hash.get(XML_AUTHENTICATION_ENTRY);
    final List<XmlValue[]> list = (List<XmlValue[]>) value.getList();
    final Iterator<XmlValue[]> iterator = list.iterator();
    File key;
    byte[] byteKeys;
    while (iterator.hasNext()) {
      final XmlValue[] subvalues = iterator.next();
      final XmlHash subHash = new XmlHash(subvalues);
      value = subHash.get(XML_AUTHENTICATION_HOSTID);
      if (value == null || value.isEmpty()) {
        continue;
      }
      final String refHostId = value.getString();
      value = subHash.get(XML_AUTHENTICATION_KEYFILE);
      if (value == null || value.isEmpty()) {
        value = subHash.get(XML_AUTHENTICATION_KEY);
        if (value == null || value.isEmpty()) {
          // Allow empty key
          byteKeys = null;
        } else {
          final String skey = value.getString();
          // key is crypted
          if (!skey.isEmpty()) {
            try {
              byteKeys = config.getCryptoKey().decryptHexInBytes(skey);
            } catch (final Exception e) {
              logger.error(
                  CANNOT_READ_KEY_FOR_HOST_ID + refHostId + ':' + skey);
              continue;
            }
          } else {
            byteKeys = null;
          }
        }
      } else {
        final String skey = value.getString();
        // load key from file
        key = new File(skey);
        if (!key.canRead()) {
          logger.error(CANNOT_READ_KEY_FOR_HOST_ID + refHostId + ':' + skey);
          continue;
        }
        try {
          byteKeys = config.getCryptoKey().decryptHexFile(key);
        } catch (final Exception e2) {
          logger.error(CANNOT_READ_KEY_FOR_HOST_ID + refHostId, e2);
          continue;
        }
      }
      boolean isAdmin = false;
      value = subHash.get(XML_AUTHENTICATION_ADMIN);
      if (value != null && !value.isEmpty()) {
        isAdmin = value.getBoolean();
      }
      value = subHash.get(XML_AUTHENTICATION_ADDRESS);
      if (value == null || value.isEmpty()) {
        continue;
      }
      final String address = value.getString();
      final int port;
      value = subHash.get(XML_AUTHENTICATION_PORT);
      if (value != null && !value.isEmpty()) {
        port = value.getInteger();
      } else {
        continue;
      }
      boolean isSsl = false;
      value = subHash.get(XML_AUTHENTICATION_ISSSL);
      if (value != null && !value.isEmpty()) {
        isSsl = value.getBoolean();
      }
      boolean isClient = false;
      value = subHash.get(XML_AUTHENTICATION_ISCLIENT);
      if (value != null && !value.isEmpty()) {
        isClient = value.getBoolean();
      }
      boolean isActive = true;
      value = subHash.get(XML_AUTHENTICATION_ISACTIVE);
      if (value != null && !value.isEmpty()) {
        isActive = value.getBoolean();
      }
      boolean isProxified = false;
      value = subHash.get(XML_AUTHENTICATION_ISPROXIFIED);
      if (value != null && !value.isEmpty()) {
        isProxified = value.getBoolean();
      }
      try {
        final DbHostAuth auth =
            new DbHostAuth(refHostId, address, port, isSsl, byteKeys, isAdmin,
                           isClient);
        auth.setActive(isActive);
        auth.setProxified(isProxified);
        if (auth.exist()) {
          auth.update();
        } else {
          auth.insert();
        }
        logger.debug("Add {} {}", refHostId, auth);
      } catch (final WaarpDatabaseException e) {
        logger.error("Cannot create Authentication for hostId {}", refHostId);
        continue;
      }
    }
    hash.clear();
    return true;
  }

  /**
   * Construct a new Element with value
   *
   * @param name
   * @param value
   *
   * @return the new Element
   */
  private static Element newElement(final String name, final String value) {
    final Element node = new DefaultElement(name);
    node.addText(value);
    return node;
  }

  /**
   * Write all authentication to a file with filename
   *
   * @param filename
   *
   * @throws OpenR66ProtocolSystemException
   * @throws WaarpDatabaseNoConnectionException
   * @throws WaarpDatabaseSqlException
   */
  public static void writeXML(final Configuration config, final String filename)
      throws OpenR66ProtocolSystemException,
             WaarpDatabaseNoConnectionException {
    final Document document = DocumentHelper.createDocument();
    final Element root = document.addElement(XML_AUTHENTICATION_ROOT);
    final DbHostAuth[] hosts = DbHostAuth.getAllHosts();
    logger.debug("Will write DbHostAuth: {} in {}", hosts.length, filename);
    for (final DbHostAuth auth : hosts) {
      logger.debug("Will write DbHostAuth: {}", auth.getHostid());
      final Element entry = new DefaultElement(XML_AUTHENTICATION_ENTRY);
      entry.add(newElement(XML_AUTHENTICATION_HOSTID, auth.getHostid()));
      final byte[] key = auth.getHostkey();
      String encode;
      try {
        encode = config.getCryptoKey().cryptToHex(key);
      } catch (final Exception e) {
        encode = "";
      }
      entry.add(newElement(XML_AUTHENTICATION_KEY, encode));
      entry.add(newElement(XML_AUTHENTICATION_ADMIN,
                           Boolean.toString(auth.isAdminrole())));
      entry.add(newElement(XML_AUTHENTICATION_ADDRESS, auth.getAddress()));
      entry.add(newElement(XML_AUTHENTICATION_PORT,
                           Integer.toString(auth.getPort())));
      entry.add(
          newElement(XML_AUTHENTICATION_ISSSL, Boolean.toString(auth.isSsl())));
      entry.add(newElement(XML_AUTHENTICATION_ISCLIENT,
                           Boolean.toString(auth.isClient())));
      entry.add(newElement(XML_AUTHENTICATION_ISACTIVE,
                           Boolean.toString(auth.isActive())));
      entry.add(newElement(XML_AUTHENTICATION_ISPROXIFIED,
                           Boolean.toString(auth.isProxified())));
      root.add(entry);
    }
    try {
      XmlUtil.writeXML(filename, null, document);
    } catch (final IOException e) {
      throw new OpenR66ProtocolSystemException("Cannot write file: " + filename,
                                               e);
    }
  }
}