R66Auth.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.context.authentication;

import org.waarp.common.command.NextCommandReply;
import org.waarp.common.command.exception.Reply421Exception;
import org.waarp.common.command.exception.Reply530Exception;
import org.waarp.common.database.exception.WaarpDatabaseException;
import org.waarp.common.database.exception.WaarpDatabaseSqlException;
import org.waarp.common.file.DirInterface;
import org.waarp.common.file.filesystembased.FilesystemBasedAuthImpl;
import org.waarp.common.logging.SysErrLogger;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import org.waarp.common.role.RoleDefault;
import org.waarp.common.role.RoleDefault.ROLE;
import org.waarp.openr66.context.R66Session;
import org.waarp.openr66.database.data.DbHostAuth;
import org.waarp.openr66.protocol.configuration.Configuration;

import java.io.File;

/**
 *
 */
public class R66Auth extends FilesystemBasedAuthImpl {
  /**
   * Internal Logger
   */
  private static final WaarpLogger logger =
      WaarpLoggerFactory.getLogger(R66Auth.class);

  /**
   * Current authentication
   */
  private DbHostAuth currentAuth;
  /**
   * is Admin role
   */
  private boolean isAdmin;
  /**
   * Role set from configuration file only
   */
  private final RoleDefault role = new RoleDefault();

  /**
   * @param session
   */
  public R66Auth(final R66Session session) {
    super(session);
  }

  /**
   * To be able to store a clone of this Authentication
   *
   * @return the clone
   */
  public R66Auth clone() {
    final R66Auth newAuth = new R66Auth((R66Session) session);
    newAuth.currentAuth = currentAuth;
    newAuth.role.clear();
    newAuth.setIsIdentified(true);
    newAuth.user = user;
    newAuth.rootFromAuth = rootFromAuth;
    newAuth.getSession().getDir().initAfterIdentification();
    newAuth.isAdmin = currentAuth.isAdminrole();
    newAuth.role.setRoleDefault(role);
    return newAuth;
  }

  /**
   * For same channel, no proxy, authentification from already authenticated
   * channel
   *
   * @param source
   *
   * @throws IllegalStateException if conditions are not valid to create a
   *     correct R66Auth
   */
  public void setFromClone(final R66Auth source) {
    if (source == null || !source.isIdentified()) {
      throw new IllegalStateException("Source Authentification not valid");
    }
    currentAuth = source.currentAuth;
    role.clear();
    setIsIdentified(true);
    user = source.user;
    rootFromAuth = source.rootFromAuth;
    getSession().getDir().initAfterIdentification();
    isAdmin = currentAuth.isAdminrole();
    role.setRoleDefault(source.role);
    logger.debug("{}", role);
  }

  @Override
  protected final void businessClean() {
    currentAuth = null;
    isAdmin = false;
    role.clear();
  }

  @Override
  public final String getBaseDirectory() {
    return Configuration.configuration.getBaseDirectory();
  }

  @Override
  protected final NextCommandReply setBusinessPassword(final String arg0)
      throws Reply421Exception {
    throw new Reply421Exception("Command not valid");
  }

  /**
   * @param hostId
   * @param arg0
   *
   * @return True if the connection is OK (authentication is OK)
   *
   * @throws Reply530Exception if the authentication is wrong
   * @throws Reply421Exception If the service is not available
   */
  public final boolean connection(final String hostId, final byte[] arg0,
                                  final boolean isSsl)
      throws Reply530Exception, Reply421Exception {
    final DbHostAuth auth;
    try {
      auth = new DbHostAuth(hostId);
    } catch (final WaarpDatabaseException e) {
      logger.error("Cannot find authentication for " + hostId);
      setIsIdentified(false);
      currentAuth = null;
      throw new Reply530Exception("HostId not allowed: " + hostId);
    }
    if (auth.isSsl() != isSsl) {
      if (auth.isSsl()) {
        logger.error("Hostid {} must use SSL", hostId);
      } else {
        logger.error("Hostid {} cannot use SSL", hostId);
      }
      setIsIdentified(false);
      currentAuth = null;
      throw new Reply530Exception("HostId not allowed: mixed Ssl");
    }
    currentAuth = auth;
    role.clear();
    if (currentAuth.isKeyValid(arg0)) {
      setIsIdentified(true);
      user = hostId;
      setRootFromAuth();
      getSession().getDir().initAfterIdentification();
      isAdmin = currentAuth.isAdminrole();
      if (Configuration.configuration.getRoles().isEmpty()) {
        if (isAdmin) {
          role.setRole(ROLE.FULLADMIN);
        } else {
          role.setRole(ROLE.PARTNER);
        }
      } else {
        final RoleDefault configRole =
            Configuration.configuration.getRoles().get(hostId);
        if (configRole == null) {
          // set to default PARTNER
          role.setRole(ROLE.PARTNER);
        } else {
          role.setRoleDefault(configRole);
          if (role.isContaining(ROLE.FULLADMIN)) {
            isAdmin = true;
          }
        }
        if (isAdmin) {
          role.setRole(ROLE.FULLADMIN);
        }
      }
      logger.debug("{}", role);
      return true;
    }
    throw new Reply530Exception("Key is not valid for this HostId");
  }

  /**
   * Set the root relative Path from current status of Authentication (should
   * be
   * the highest level for the
   * current authentication). If setBusinessRootFromAuth returns null, by
   * default set /user.
   *
   * @throws Reply421Exception if the business root is not available
   */
  @Override
  protected final void setRootFromAuth() throws Reply421Exception {
    rootFromAuth = setBusinessRootFromAuth();
    if (rootFromAuth == null) {
      rootFromAuth = DirInterface.SEPARATOR;
    }
  }

  @Override
  protected final String setBusinessRootFromAuth() throws Reply421Exception {
    final String path = null;
    final String fullpath = getAbsolutePath(null);
    final File file = new File(fullpath);
    if (!file.isDirectory()) {
      throw new Reply421Exception("Filesystem not ready");
    }
    return path;
  }

  @Override
  protected final NextCommandReply setBusinessUser(final String arg0)
      throws Reply421Exception {
    throw new Reply421Exception("Command not valid");
  }

  @Override
  public final boolean isAdmin() {
    return isAdmin;
  }

  /**
   * @param roleCheck
   *
   * @return True if the current role contains the specified role to check
   */
  public final boolean isValidRole(final ROLE roleCheck) {
    final ROLE[] roles = roleCheck.getComposingRoles();
    for (final ROLE role1 : roles) {
      if (!role.isContaining(role1)) {
        return false;
      }
    }
    return true;
  }

  /**
   * @return True if the associated host is using SSL
   */
  public final boolean isSsl() {
    return currentAuth.isSsl();
  }

  @Override
  public final boolean isBusinessPathValid(final String newPath) {
    return newPath != null;
  }

  @Override
  public String toString() {
    return "Auth:" + isIdentified + ' ' +
           (currentAuth != null? currentAuth.toString() : "no Internal Auth") +
           ' ' + role;
  }

  /**
   * @param server
   *
   * @return the SimpleAuth if any for this user
   */
  public static DbHostAuth getServerAuth(final String server) {
    final DbHostAuth auth;
    try {
      auth = new DbHostAuth(server);
    } catch (final WaarpDatabaseException e) {
      if (!server.equals(Configuration.configuration.getHostId())) {
        logger.warn("Cannot find the authentication " + server + " : {}",
                    e.getMessage());
      }
      return null;
    }
    return auth;
  }

  /**
   * Special Authentication for local execution
   *
   * @param isSSL
   * @param hostid
   */
  public final void specialNoSessionAuth(final boolean isSSL,
                                         final String hostid) {
    isIdentified = true;
    DbHostAuth auth = null;
    try {
      auth = new DbHostAuth(hostid);
    } catch (final WaarpDatabaseException ignored) {
      // nothing
    }
    if (auth == null) {
      try {
        auth =
            new DbHostAuth(hostid, "127.0.0.1", 6666, isSSL, null, true, false);
      } catch (final WaarpDatabaseSqlException e) {
        SysErrLogger.FAKE_LOGGER.syserr(e);
      }
    }
    role.clear();
    currentAuth = auth;
    setIsIdentified(true);
    user = auth.getHostid(); // NOSONAR
    try {
      setRootFromAuth();
    } catch (final Reply421Exception ignored) {
      // nothing
    }
    getSession().getDir().initAfterIdentification();
    isAdmin = isSSL;
    if (isSSL) {
      role.setRole(ROLE.FULLADMIN);
      user = Configuration.configuration.getAdminName();
    }
  }

  /**
   * connection from HTTPS (no default rights, must be set either as admin or
   * specifically through ROLEs). Only
   * "false client" with port with negative values are allowed.
   *
   * @param hostId
   * @param arg0
   *
   * @return True if the connection is OK (authentication is OK)
   *
   * @throws Reply530Exception if the authentication is wrong
   * @throws Reply421Exception If the service is not available
   */
  public final boolean connectionHttps(final String hostId, final byte[] arg0)
      throws Reply530Exception, Reply421Exception {
    final DbHostAuth auth = getServerAuth(hostId);
    if (auth == null) {
      logger.error("Cannot find authentication for " + hostId);
      setIsIdentified(false);
      currentAuth = null;
      throw new Reply530Exception("HostId not allowed");
    }
    if (auth.getPort() >= 0) {
      logger.error("Authentication is unacceptable for " + hostId);
      setIsIdentified(false);
      currentAuth = null;
      throw new Reply530Exception("HostId not allowed");
    }
    role.clear();
    currentAuth = auth;
    if (currentAuth.isKeyValid(arg0)) {
      setIsIdentified(true);
      user = hostId;
      setRootFromAuth();
      getSession().getDir().initAfterIdentification();
      isAdmin = currentAuth.isAdminrole();
      if (!Configuration.configuration.getRoles().isEmpty()) {
        final RoleDefault configRole =
            Configuration.configuration.getRoles().get(hostId);
        if (configRole != null) {
          role.setRoleDefault(configRole);
          if (role.isContaining(ROLE.FULLADMIN)) {
            isAdmin = true;
          }
        }
      }
      if (isAdmin) {
        role.setRole(ROLE.FULLADMIN);
      }
      logger.info("{}:{}", role, currentAuth);
      return true;
    }
    throw new Reply530Exception("Key is not valid for this HostId");
  }

  /**
   * @return a copy of the Role of the current authenticated partner
   */
  public final RoleDefault getRole() {
    return new RoleDefault().setRoleDefault(role);
  }
}