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

com.orientechnologies.orient.server.OServer Maven / Gradle / Ivy

There is a newer version: 3.2.41
Show newest version
/*
 *
 *  *  Copyright 2014 Orient Technologies LTD (info(at)orientechnologies.com)
 *  *
 *  *  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.
 *  *
 *  * For more information: http://www.orientechnologies.com
 *
 */
package com.orientechnologies.orient.server;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.ReentrantLock;

import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;

import com.orientechnologies.common.console.OConsoleReader;
import com.orientechnologies.common.console.ODefaultConsoleReader;
import com.orientechnologies.common.exception.OException;
import com.orientechnologies.common.io.OFileUtils;
import com.orientechnologies.common.io.OIOUtils;
import com.orientechnologies.common.log.OAnsiCode;
import com.orientechnologies.common.log.OLogManager;
import com.orientechnologies.common.parser.OSystemVariableResolver;
import com.orientechnologies.common.profiler.OAbstractProfiler.OProfilerHookValue;
import com.orientechnologies.common.profiler.OProfiler.METRIC_TYPE;
import com.orientechnologies.orient.core.OConstants;
import com.orientechnologies.orient.core.Orient;
import com.orientechnologies.orient.core.config.OContextConfiguration;
import com.orientechnologies.orient.core.config.OGlobalConfiguration;
import com.orientechnologies.orient.core.db.ODatabase;
import com.orientechnologies.orient.core.db.ODatabaseInternal;
import com.orientechnologies.orient.core.db.OPartitionedDatabasePoolFactory;
import com.orientechnologies.orient.core.db.document.ODatabaseDocument;
import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
import com.orientechnologies.orient.core.exception.OConfigurationException;
import com.orientechnologies.orient.core.exception.ODatabaseException;
import com.orientechnologies.orient.core.exception.OSecurityException;
import com.orientechnologies.orient.core.exception.OStorageException;
import com.orientechnologies.orient.core.metadata.security.ORole;
import com.orientechnologies.orient.core.metadata.security.OToken;
import com.orientechnologies.orient.core.metadata.security.OUser;
import com.orientechnologies.orient.core.security.OSecurityManager;
import com.orientechnologies.orient.core.storage.OStorage;
import com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage;
import com.orientechnologies.orient.core.storage.impl.local.paginated.OLocalPaginatedStorage;
import com.orientechnologies.orient.core.storage.impl.memory.ODirectMemoryStorage;
import com.orientechnologies.orient.server.config.*;
import com.orientechnologies.orient.server.distributed.ODistributedServerManager;
import com.orientechnologies.orient.server.handler.OConfigurableHooksManager;
import com.orientechnologies.orient.server.network.OServerNetworkListener;
import com.orientechnologies.orient.server.network.OServerSocketFactory;
import com.orientechnologies.orient.server.network.protocol.ONetworkProtocol;
import com.orientechnologies.orient.server.network.protocol.ONetworkProtocolData;
import com.orientechnologies.orient.server.plugin.OServerPlugin;
import com.orientechnologies.orient.server.plugin.OServerPluginInfo;
import com.orientechnologies.orient.server.plugin.OServerPluginManager;
import com.orientechnologies.orient.server.security.ODefaultServerSecurity;
import com.orientechnologies.orient.server.security.OSecurityServerUser;
import com.orientechnologies.orient.server.security.OServerSecurity;
import com.orientechnologies.orient.server.token.OTokenHandlerImpl;

public class OServer {
  private static final String                              ROOT_PASSWORD_VAR      = "ORIENTDB_ROOT_PASSWORD";
  private static ThreadGroup                               threadGroup;
  private static Map                      distributedServers     = new ConcurrentHashMap();
  private final CountDownLatch                             startupLatch           = new CountDownLatch(1);
  private final boolean                                    shutdownEngineOnExit;
  protected ReentrantLock                                  lock                   = new ReentrantLock();
  protected volatile boolean                               running                = false;
  protected OServerConfigurationManager                    serverCfg;
  protected OContextConfiguration                          contextConfiguration;
  protected OServerShutdownHook                            shutdownHook;
  protected Map> networkProtocols       = new HashMap>();
  protected Map              networkSocketFactories = new HashMap();
  protected List                   networkListeners       = new ArrayList();
  protected List                 lifecycleListeners     = new ArrayList();
  protected OServerPluginManager                           pluginManager;
  protected OConfigurableHooksManager                      hookManager;
  protected ODistributedServerManager                      distributedManager;
  protected OServerSecurity                                serverSecurity;
  private OPartitionedDatabasePoolFactory                  dbPoolFactory;
  private SecureRandom                                     random                 = new SecureRandom();
  private Map                              variables              = new HashMap();
  private String                                           serverRootDirectory;
  private String                                           databaseDirectory;
  private OClientConnectionManager                         clientConnectionManager;
  private ClassLoader                                      extensionClassLoader;
  private OTokenHandler                                    tokenHandler;
  private OSystemDatabase                                  systemDatabase;

  public OServer() throws ClassNotFoundException, MalformedObjectNameException, NullPointerException,
      InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException {
    this(true);
  }

  public OServer(boolean shutdownEngineOnExit) throws ClassNotFoundException, MalformedObjectNameException, NullPointerException,
      InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException {
    this.shutdownEngineOnExit = shutdownEngineOnExit;

    serverRootDirectory = OSystemVariableResolver.resolveSystemVariables("${" + Orient.ORIENTDB_HOME + "}", ".");

    OLogManager.instance().installCustomFormatter();

    defaultSettings();

    threadGroup = new ThreadGroup("OrientDB Server");

    System.setProperty("com.sun.management.jmxremote", "true");

    Orient.instance().startup();

    if (OGlobalConfiguration.PROFILER_ENABLED.getValueAsBoolean() && !Orient.instance().getProfiler().isRecording())
      Orient.instance().getProfiler().startRecording();

    shutdownHook = new OServerShutdownHook(this);
  }

  public static OServer getInstance(final String iServerId) {
    return distributedServers.get(iServerId);
  }

  public static OServer getInstanceByPath(final String iPath) {
    for (Map.Entry entry : distributedServers.entrySet()) {
      if (iPath.startsWith(entry.getValue().getDatabaseDirectory()))
        return entry.getValue();
    }
    return null;
  }

  public static void registerServerInstance(final String iServerId, final OServer iServer) {
    distributedServers.put(iServerId, iServer);
  }

  /**
   * Set the preferred {@link ClassLoader} used to load extensions.
   *
   * @since 2.1
   */
  public void setExtensionClassLoader(/* @Nullable */final ClassLoader extensionClassLoader) {
    this.extensionClassLoader = extensionClassLoader;
  }

  /**
   * Get the preferred {@link ClassLoader} used to load extensions.
   *
   * @since 2.1
   */
  /* @Nullable */
  public ClassLoader getExtensionClassLoader() {
    return extensionClassLoader;
  }

  public OServerSecurity getSecurity() {
    return serverSecurity;
  }

  public boolean isActive() {
    return running;
  }

  public OClientConnectionManager getClientConnectionManager() {
    return clientConnectionManager;
  }

  public void saveConfiguration() throws IOException {
    serverCfg.saveConfiguration();
  }

  public void restart() throws ClassNotFoundException, InvocationTargetException, InstantiationException, NoSuchMethodException,
      IllegalAccessException, IOException {
    try {
      shutdown();
    } finally {
      startup(serverCfg.getConfiguration());
      activate();
    }
  }

  public OSystemDatabase getSystemDatabase() {
    return systemDatabase;
  }

  /**
   * Load an extension class by name.
   */
  private Class loadClass(final String name) throws ClassNotFoundException {
    Class loaded = tryLoadClass(extensionClassLoader, name);
    if (loaded == null) {
      loaded = tryLoadClass(Thread.currentThread().getContextClassLoader(), name);
      if (loaded == null) {
        loaded = tryLoadClass(getClass().getClassLoader(), name);
        if (loaded == null) {
          loaded = Class.forName(name);
        }
      }
    }
    return loaded;
  }

  /**
   * Attempt to load a class from givenstar class-loader.
   */
  /* @Nullable */
  private Class tryLoadClass(/* @Nullable */final ClassLoader classLoader, final String name) {
    if (classLoader != null) {
      try {
        return classLoader.loadClass(name);
      } catch (ClassNotFoundException e) {
        // ignore
      }
    }
    return null;
  }

  public OServer startup() throws InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException,
      SecurityException, InvocationTargetException, NoSuchMethodException {
    String config = OServerConfiguration.DEFAULT_CONFIG_FILE;
    if (System.getProperty(OServerConfiguration.PROPERTY_CONFIG_FILE) != null)
      config = System.getProperty(OServerConfiguration.PROPERTY_CONFIG_FILE);

    Orient.instance().startup();

    startup(new File(OSystemVariableResolver.resolveSystemVariables(config)));

    return this;
  }

  public OServer startup(final File iConfigurationFile) throws InstantiationException, IllegalAccessException,
      ClassNotFoundException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException {
    // Startup function split to allow pre-activation changes
    try {
      serverCfg = new OServerConfigurationManager(iConfigurationFile);
      return startupFromConfiguration();

    } catch (IOException e) {
      final String message = "Error on reading server configuration from file: " + iConfigurationFile;
      OLogManager.instance().error(this, message, e);
      throw OException.wrapException(new OConfigurationException(message), e);
    }
  }

  public OServer startup(final String iConfiguration) throws InstantiationException, IllegalAccessException, ClassNotFoundException,
      IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException, IOException {
    return startup(new ByteArrayInputStream(iConfiguration.getBytes()));
  }

  public OServer startup(final InputStream iInputStream)
      throws InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, SecurityException,
      InvocationTargetException, NoSuchMethodException, IOException {
    if (iInputStream == null)
      throw new OConfigurationException("Configuration file is null");

    serverCfg = new OServerConfigurationManager(iInputStream);

    // Startup function split to allow pre-activation changes
    return startupFromConfiguration();
  }

  public OServer startup(final OServerConfiguration iConfiguration)
      throws IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException, IOException {
    serverCfg = new OServerConfigurationManager(iConfiguration);
    return startupFromConfiguration();
  }

  public OServer startupFromConfiguration()
      throws IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException, IOException {
    OLogManager.instance().info(this, "OrientDB Server v" + OConstants.getVersion() + " is starting up...");

    Orient.instance();

    clientConnectionManager = new OClientConnectionManager();

    initFromConfiguration();

    if (OGlobalConfiguration.ENVIRONMENT_DUMP_CFG_AT_STARTUP.getValueAsBoolean()) {
      System.out.println("Dumping environment after server startup...");
      OGlobalConfiguration.dumpConfiguration(System.out);
    }

    dbPoolFactory = new OPartitionedDatabasePoolFactory();
    dbPoolFactory.setMaxPoolSize(contextConfiguration.getValueAsInteger(OGlobalConfiguration.DB_POOL_MAX));

    databaseDirectory = contextConfiguration.getValue("server.database.path", serverRootDirectory + "/databases/");
    databaseDirectory = OFileUtils.getPath(OSystemVariableResolver.resolveSystemVariables(databaseDirectory));
    databaseDirectory = databaseDirectory.replace("//", "/");

    // CONVERT IT TO ABSOLUTE PATH
    databaseDirectory = (new File(databaseDirectory)).getCanonicalPath();
    databaseDirectory = OFileUtils.getPath(databaseDirectory);

    if (!databaseDirectory.endsWith("/"))
      databaseDirectory += "/";

    OLogManager.instance().info(this, "Databases directory: " + new File(databaseDirectory).getAbsolutePath());

    Orient.instance().getProfiler().registerHookValue("system.databases", "List of databases configured in Server",
        METRIC_TYPE.TEXT, new OProfilerHookValue() {
          @Override
          public Object getValue() {
            final StringBuilder dbs = new StringBuilder(64);
            for (String dbName : getAvailableStorageNames().keySet()) {
              if (dbs.length() > 0)
                dbs.append(',');
              dbs.append(dbName);
            }
            return dbs.toString();
          }
        });

    return this;
  }

  @SuppressWarnings("unchecked")
  public OServer activate() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
    try {
      serverSecurity = new ODefaultServerSecurity(this, serverCfg);
      Orient.instance().setSecurity(serverSecurity);

      // Checks to see if the OrientDB System Database exists and creates it if not.
      // Make sure this happens after setSecurityFactory() is called.
      initSystemDatabase();

      for (OServerLifecycleListener l : lifecycleListeners)
        l.onBeforeActivate();

      final OServerConfiguration configuration = serverCfg.getConfiguration();

      if (configuration.network != null) {
        // REGISTER/CREATE SOCKET FACTORIES
        if (configuration.network.sockets != null) {
          for (OServerSocketFactoryConfiguration f : configuration.network.sockets) {
            Class fClass = (Class) loadClass(f.implementation);
            OServerSocketFactory factory = fClass.newInstance();
            try {
              factory.config(f.name, f.parameters);
              networkSocketFactories.put(f.name, factory);
            } catch (OConfigurationException e) {
              OLogManager.instance().error(this, "Error creating socket factory", e);
            }
          }
        }

        // REGISTER PROTOCOLS
        for (OServerNetworkProtocolConfiguration p : configuration.network.protocols)
          networkProtocols.put(p.name, (Class) loadClass(p.implementation));

        // STARTUP LISTENERS
        for (OServerNetworkListenerConfiguration l : configuration.network.listeners)
          networkListeners.add(new OServerNetworkListener(this, networkSocketFactories.get(l.socket), l.ipAddress, l.portRange,
              l.protocol, networkProtocols.get(l.protocol), l.parameters, l.commands));

      } else
        OLogManager.instance().warn(this, "Network configuration was not found");

      try {
        loadStorages();
        loadUsers();
        loadDatabases();
      } catch (IOException e) {
        final String message = "Error on reading server configuration";
        OLogManager.instance().error(this, message, e);

        throw OException.wrapException(new OConfigurationException(message), e);
      }

      tokenHandler = new OTokenHandlerImpl(this);

      registerPlugins();

      for (OServerLifecycleListener l : lifecycleListeners)
        l.onAfterActivate();

      running = true;

      OLogManager.instance().info(this, "$ANSI{green:italic OrientDB Server is active} v" + OConstants.getVersion() + ".");
    } catch (ClassNotFoundException e) {
      running = false;
      throw e;
    } catch (InstantiationException e) {
      running = false;
      throw e;
    } catch (IllegalAccessException e) {
      running = false;
      throw e;
    } catch (RuntimeException e) {
      running = false;
      throw e;
    } finally {
      startupLatch.countDown();
    }

    return this;
  }

  public void removeShutdownHook() {
    if (shutdownHook != null) {
      shutdownHook.cancel();
      shutdownHook = null;
    }
  }

  public boolean shutdown() {
    if (!running)
      return false;

    try {
      running = false;

      OLogManager.instance().info(this, "OrientDB Server is shutting down...");

      if (shutdownHook != null)
        shutdownHook.cancel();

      Orient.instance().getProfiler().unregisterHookValue("system.databases");

      for (OServerLifecycleListener l : lifecycleListeners)
        l.onBeforeDeactivate();

      lock.lock();
      try {
        if (networkListeners.size() > 0) {
          // SHUTDOWN LISTENERS
          OLogManager.instance().info(this, "Shutting down listeners:");
          // SHUTDOWN LISTENERS
          for (OServerNetworkListener l : networkListeners) {
            OLogManager.instance().info(this, "- %s", l);
            try {
              l.shutdown();
            } catch (Throwable e) {
              OLogManager.instance().error(this, "Error during shutdown of listener %s.", e, l);
            }
          }
        }

        if (networkProtocols.size() > 0) {
          // PROTOCOL SHUTDOWN
          OLogManager.instance().info(this, "Shutting down protocols");
          networkProtocols.clear();
        }

        for (OServerLifecycleListener l : lifecycleListeners)
          try {
            l.onAfterDeactivate();
          } catch (Exception e) {
            OLogManager.instance().error(this, "Error during deactivation of server lifecycle listener %s", e, l);
          }

        clientConnectionManager.shutdown();

        if (pluginManager != null)
          pluginManager.shutdown();

      } finally {
        lock.unlock();
      }

      if (shutdownEngineOnExit && !Orient.isRegisterDatabaseByPath())
        try {
          OLogManager.instance().info(this, "Shutting down databases:");
          Orient.instance().shutdown();
        } catch (Throwable e) {
          OLogManager.instance().error(this, "Error during OrientDB shutdown", e);
        }
    } finally {
      OLogManager.instance().info(this, "OrientDB Server shutdown complete\n");
      OLogManager.instance().flush();
    }

    return true;
  }

  public String getStoragePath(final String iName) {
    if (iName == null)
      throw new IllegalArgumentException("Storage path is null");

    final String name = iName.indexOf(':') > -1 ? iName.substring(iName.indexOf(':') + 1) : iName;

    final String dbName = Orient.isRegisterDatabaseByPath() ? getDatabaseDirectory() + name : name;
    final String dbPath = Orient.isRegisterDatabaseByPath() ? dbName : getDatabaseDirectory() + name;

    if (dbPath.contains(".."))
      throw new IllegalArgumentException("Storage path is invalid because it contains '..'");

    if (dbPath.contains("*"))
      throw new IllegalArgumentException("Storage path is invalid because of the wildcard '*'");

    if (dbPath.startsWith("/")) {
      if (!dbPath.startsWith(getDatabaseDirectory()))
        throw new IllegalArgumentException("Storage path is invalid because it points to an absolute directory");
    }

    final OStorage stg = Orient.instance().getStorage(dbName);
    if (stg != null)
      // ALREADY OPEN
      return stg.getURL();

    // SEARCH IN CONFIGURED PATHS
    final OServerConfiguration configuration = serverCfg.getConfiguration();
    String dbURL = configuration.getStoragePath(name);
    if (dbURL == null) {
      // SEARCH IN DEFAULT DATABASE DIRECTORY
      if (new File(OIOUtils.getPathFromDatabaseName(dbPath) + "/database.ocf").exists())
        dbURL = "plocal:" + dbPath;
      else
        throw new OConfigurationException(
            "Database '" + name + "' is not configured on server (home=" + getDatabaseDirectory() + ")");
    }

    return dbURL;
  }

  public Map getAvailableStorageNames() {
    final OServerConfiguration configuration = serverCfg.getConfiguration();

    // SEARCH IN CONFIGURED PATHS
    final Map storages = new HashMap();
    if (configuration.storages != null && configuration.storages.length > 0)
      for (OServerStorageConfiguration s : configuration.storages)
        storages.put(OIOUtils.getDatabaseNameFromPath(s.name), s.path);

    // SEARCH IN DEFAULT DATABASE DIRECTORY
    final String rootDirectory = getDatabaseDirectory();
    scanDatabaseDirectory(new File(rootDirectory), storages);

    for (OStorage storage : Orient.instance().getStorages()) {
      final String storageUrl = storage.getURL();
      // TEST IT'S OF CURRENT SERVER INSTANCE BY CHECKING THE PATH
      if (storage instanceof OAbstractPaginatedStorage && storage.exists() && !storages.containsValue(storageUrl)
          && isStorageOfCurrentServerInstance(storage))
        storages.put(OIOUtils.getDatabaseNameFromPath(storage.getName()), storageUrl);
    }

    if (storages != null)
      storages.remove(OSystemDatabase.SYSTEM_DB_NAME);

    return storages;
  }

  /**
   * Opens all the available server's databases.
   */
  protected void loadDatabases() {
    if (!OGlobalConfiguration.SERVER_OPEN_ALL_DATABASES_AT_STARTUP.getValueAsBoolean())
      return;

    final String dbPath = getDatabaseDirectory();
    for (Map.Entry storageEntry : getAvailableStorageNames().entrySet()) {
      final String databaseName = storageEntry.getKey();

      OLogManager.instance().info(this, "Opening database '%s' at startup...", databaseName);

      final ODatabaseDocumentTx db = new ODatabaseDocumentTx("plocal:" + dbPath + databaseName);
      try {
        try {
          openDatabaseBypassingSecurity(db, null, "internal");
        } catch (OStorageException e) {
          if (e.getCause() instanceof OSecurityException) {
            if (askForEncryptionKey(databaseName)) {
              // RETRY IT
              try {
                openDatabaseBypassingSecurity(db, null, "internal");
              } catch (Exception e2) {
                // LOOK FOR A SECURITY EXCEPTION
                Throwable nested = e2;
                while (nested != null) {
                  if (nested instanceof OSecurityException) {
                    OLogManager.instance().error(this, "Invalid key for database '%s'. Skip database opening", databaseName);
                    return;
                  }
                  nested = nested.getCause();
                }
                OLogManager.instance().error(this, "Error on opening database '%s': %s", e, e.getMessage());
              }
            }
          }
        }
      } finally {
        db.activateOnCurrentThread();
        db.close();
      }
    }
  }

  private boolean askForEncryptionKey(final String iDatabaseName) {
    try {
      Thread.sleep(500);
    } catch (InterruptedException e) {
    }

    System.out.println();
    System.out.println();
    System.out
        .println(OAnsiCode.format("$ANSI{yellow +--------------------------------------------------------------------------+}"));
    System.out.println(OAnsiCode
        .format(String.format("$ANSI{yellow | INSERT THE KEY FOR THE ENCRYPTED DATABASE %-31s|}", "'" + iDatabaseName + "'")));
    System.out
        .println(OAnsiCode.format("$ANSI{yellow +--------------------------------------------------------------------------+}"));
    System.out
        .println(OAnsiCode.format("$ANSI{yellow | To avoid this message set the environment variable or JVM setting        |}"));
    System.out
        .println(OAnsiCode.format("$ANSI{yellow | 'storage.encryptionKey' to the key to use.                               |}"));
    System.out
        .println(OAnsiCode.format("$ANSI{yellow +--------------------------------------------------------------------------+}"));
    System.out.print(OAnsiCode.format("\n$ANSI{yellow Database encryption key [BLANK=to skip opening]: }"));

    final OConsoleReader reader = new ODefaultConsoleReader();
    try {
      String key = reader.readPassword();
      if (key != null) {
        key = key.trim();
        if (!key.isEmpty()) {
          OGlobalConfiguration.STORAGE_ENCRYPTION_KEY.setValue(key);
          return true;
        }
      }
    } catch (IOException e) {
    }
    return false;
  }

  public String getDatabaseDirectory() {
    return databaseDirectory;
  }

  public ThreadGroup getServerThreadGroup() {
    return threadGroup;
  }

  public OServerUserConfiguration serverLogin(final String iUser, final String iPassword, final String iResource) {
    // Returns null if authentication or authorization fails for any reason.
    return authenticateUser(iUser, iPassword, iResource);
  }

  /**
   * Authenticate a server user.
   *
   * @param iUserName
   *          Username to authenticate
   * @param iPassword
   *          Password in clear
   * @return true if authentication is ok, otherwise false
   */
  public boolean authenticate(final String iUserName, final String iPassword, final String iResourceToCheck) {
    // FALSE INDICATES WRONG PASSWORD OR NO AUTHORIZATION
    return authenticateUser(iUserName, iPassword, iResourceToCheck) != null;
  }

  // Returns null if the user cannot be authenticated. Otherwise returns the OServerUserConfiguration user.
  protected OServerUserConfiguration authenticateUser(final String iUserName, final String iPassword,
      final String iResourceToCheck) {
    if (serverSecurity != null && serverSecurity.isEnabled()) {
      // Returns the authenticated username, if successful, otherwise null.
      String authUsername = serverSecurity.authenticate(iUserName, iPassword);

      // Authenticated, now see if the user is authorized.
      if (authUsername != null) {
        if (serverSecurity.isAuthorized(authUsername, iResourceToCheck)) {
          return serverSecurity.getUser(authUsername);
        }
      }
    } else {
      OServerUserConfiguration user = getUser(iUserName);

      if (user != null && user.password != null) {
        if (OSecurityManager.instance().checkPassword(iPassword, user.password) && isAllowed(iUserName, iResourceToCheck)) {
          return user;
        }
      }
    }

    return null;
  }

  /**
   * Checks if a server user is allowed to operate with a resource.
   *
   * @param iUserName
   *          Username to authenticate
   * @return true if authentication is ok, otherwise false
   */
  public boolean isAllowed(final String iUserName, final String iResourceToCheck) {

    if (serverSecurity != null && serverSecurity.isEnabled()) {
      // Let the security plug-in check its users list first.
      if (serverSecurity.isAuthorized(iUserName, iResourceToCheck))
        return true;
    } else {
      final OServerUserConfiguration user = getUser(iUserName);

      if (user != null) {
        if (user.resources.equals("*"))
          // ACCESS TO ALL
          return true;

        String[] resourceParts = user.resources.split(",");
        for (String r : resourceParts)
          if (r.equals(iResourceToCheck))
            return true;
      }
    }

    // WRONG PASSWORD OR NO AUTHORIZATION
    return false;
  }

  public OServerUserConfiguration getUser(final String iUserName) {
    OServerUserConfiguration userCfg = null;

    // First see if iUserName is a security plugin user.
    if (serverSecurity != null && serverSecurity.isEnabled()) {
      userCfg = serverSecurity.getUser(iUserName);
    } else {
      // This will throw an IllegalArgumentException if iUserName is null or empty.
      // However, a null or empty iUserName is possible with some security implementations.
      if (iUserName != null && !iUserName.isEmpty())
        userCfg = serverCfg.getUser(iUserName);
    }

    return userCfg;
  }

  public void dropUser(final String iUserName) throws IOException {
    serverCfg.dropUser(iUserName);
    serverCfg.saveConfiguration();
  }

  public boolean existsStoragePath(final String iURL) {
    return serverCfg.getConfiguration().getStoragePath(iURL) != null;
  }

  public OServerConfiguration getConfiguration() {
    return serverCfg.getConfiguration();
  }

  public Map> getNetworkProtocols() {
    return networkProtocols;
  }

  public List getNetworkListeners() {
    return networkListeners;
  }

  @SuppressWarnings("unchecked")
  public  RET getListenerByProtocol(final Class iProtocolClass) {
    for (OServerNetworkListener l : networkListeners)
      if (iProtocolClass.isAssignableFrom(l.getProtocolType()))
        return (RET) l;

    return null;
  }

  public Collection getPlugins() {
    return pluginManager != null ? pluginManager.getPlugins() : null;
  }

  public OContextConfiguration getContextConfiguration() {
    return contextConfiguration;
  }

  @SuppressWarnings("unchecked")
  public  RET getPluginByClass(final Class iPluginClass) {
    try {
      startupLatch.await();
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
    }
    if (!running)
      throw new ODatabaseException("Error on plugin lookup the server didn't start correcty.");

    for (OServerPluginInfo h : getPlugins())
      if (h.getInstance() != null && h.getInstance().getClass().equals(iPluginClass))
        return (RET) h.getInstance();

    return null;
  }

  @SuppressWarnings("unchecked")
  public  RET getPlugin(final String iName) {
    try {
      startupLatch.await();
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
    }
    if (!running)
      throw new ODatabaseException("Error on plugin lookup: the server did not start correctly");

    final OServerPluginInfo p = pluginManager.getPluginByName(iName);
    if (p != null)
      return (RET) p.getInstance();
    return null;
  }

  public Object getVariable(final String iName) {
    return variables.get(iName);
  }

  public OServer setVariable(final String iName, final Object iValue) {
    if (iValue == null)
      variables.remove(iName);
    else
      variables.put(iName, iValue);
    return this;
  }

  public void addTemporaryUser(final String iName, final String iPassword, final String iPermissions) {
    serverCfg.setEphemeralUser(iName, iPassword, iPermissions);
  }

  public void addUser(final String iName, String iPassword, final String iPermissions) throws IOException {
    if (iPassword == null) {
      // AUTO GENERATE PASSWORD
      final byte[] buffer = new byte[32];
      random.nextBytes(buffer);
      iPassword = OSecurityManager.instance().createSHA256(OSecurityManager.byteArrayToHexStr(buffer));
    }

    // HASH THE PASSWORD
    iPassword = OSecurityManager.instance().createHash(iPassword,
        getContextConfiguration().getValueAsString(OGlobalConfiguration.SECURITY_USER_PASSWORD_DEFAULT_ALGORITHM), true);

    serverCfg.setUser(iName, iPassword, iPermissions);
    serverCfg.saveConfiguration();
  }

  public OServer registerLifecycleListener(final OServerLifecycleListener iListener) {
    lifecycleListeners.add(iListener);
    return this;
  }

  public OServer unregisterLifecycleListener(final OServerLifecycleListener iListener) {
    lifecycleListeners.remove(iListener);
    return this;
  }

  public ODatabase openDatabase(final String iDbUrl, final OToken iToken) {
    final String path = getStoragePath(iDbUrl);

    final ODatabaseInternal database = new ODatabaseDocumentTx(path);
    if (database.isClosed()) {
      final OStorage storage = database.getStorage();
      if (storage instanceof ODirectMemoryStorage && !storage.exists())
        database.create();
      else
        database.open(iToken);
    }

    return database;
  }

  public ODatabase openDatabase(final String iDbUrl, final String user, final String password) {
    return openDatabase(iDbUrl, user, password, null, false);
  }

  public ODatabase openDatabase(final String iDbUrl, final String user, final String password, ONetworkProtocolData data) {
    return openDatabase(iDbUrl, user, password, data, false);
  }

  public ODatabaseDocumentTx openDatabase(final String iDbUrl, final String user, final String password, ONetworkProtocolData data,
      final boolean iBypassAccess) {
    final String path = getStoragePath(iDbUrl);

    final ODatabaseDocumentTx database = new ODatabaseDocumentTx(path);

    return openDatabase(database, user, password, data, iBypassAccess);
  }

  public ODatabaseDocumentTx openDatabase(final ODatabaseDocumentTx database, final String user, final String password,
      final ONetworkProtocolData data, final boolean iBypassAccess) {
    final OStorage storage = database.getStorage();
    if (database.isClosed()) {
      if (storage instanceof ODirectMemoryStorage && !storage.exists()) {
        try {
          database.create();
        } catch (OStorageException e) {
        }
      } else {
        if (iBypassAccess) {
          // BYPASS SECURITY
          openDatabaseBypassingSecurity(database, data, user);
        } else {
          // TRY WITH SERVER'S AUTHENTICATION
          OServerUserConfiguration serverUser = serverLogin(user, password, "database.passthrough");

          if (serverUser != null) {
            // Why do we use the returned serverUser name instead of just passing-in user?
            // Because in some security implementations the user is embedded inside a ticket of some kind
            // that must be decrypted to retrieve the actual user identity. If serverLogin() is successful,
            // that user identity is returned.

            // SERVER AUTHENTICATED, BYPASS SECURITY
            openDatabaseBypassingSecurity(database, data, serverUser.name);
          } else {
            // TRY DATABASE AUTHENTICATION
            database.open(user, password);
            if (data != null) {
              data.serverUser = false;
              data.serverUsername = null;
            }
          }
        }
      }
    }

    return database;
  }

  public void openDatabaseBypassingSecurity(final ODatabaseInternal database, final ONetworkProtocolData data,
      final String user) {
    database.activateOnCurrentThread();
    database.resetInitialization();
    database.setProperty(ODatabase.OPTIONS.SECURITY.toString(), OSecurityServerUser.class);
    database.open(user, "nopassword");
    if (data != null) {
      data.serverUser = true;
      data.serverUsername = user;
    }
  }

  public ODatabaseInternal openDatabase(final ODatabaseInternal database) {
    database.activateOnCurrentThread();

    if (database.isClosed())
      if (database.getStorage() instanceof ODirectMemoryStorage)
        database.create();
      else {
        // SERVER AUTHENTICATED, BYPASS SECURITY
        openDatabaseBypassingSecurity(database, null, "internal");
      }

    return database;
  }

  public ODistributedServerManager getDistributedManager() {
    return distributedManager;
  }

  public OPartitionedDatabasePoolFactory getDatabasePoolFactory() {
    return dbPoolFactory;
  }

  public void setServerRootDirectory(final String rootDirectory) {
    this.serverRootDirectory = rootDirectory;
  }

  protected void initFromConfiguration() {
    final OServerConfiguration cfg = serverCfg.getConfiguration();

    // FILL THE CONTEXT CONFIGURATION WITH SERVER'S PARAMETERS
    contextConfiguration = new OContextConfiguration();

    if (cfg.properties != null)
      for (OServerEntryConfiguration prop : cfg.properties)
        contextConfiguration.setValue(prop.name, prop.value);

    hookManager = new OConfigurableHooksManager(cfg);
  }

  protected void loadUsers() throws IOException {
    final OServerConfiguration configuration = serverCfg.getConfiguration();

    if (configuration.isAfterFirstTime) {
      return;
    }

    configuration.isAfterFirstTime = true;

    if (OGlobalConfiguration.CREATE_DEFAULT_USERS.getValueAsBoolean())
      createDefaultServerUsers();
  }

  /**
   * Load configured storages.
   */
  protected void loadStorages() {
    final OServerConfiguration configuration = serverCfg.getConfiguration();

    if (configuration.storages == null)
      return;

    String type;
    for (OServerStorageConfiguration stg : configuration.storages)
      if (stg.loadOnStartup) {
        // @COMPATIBILITY
        if (stg.userName == null)
          stg.userName = OUser.ADMIN;
        if (stg.userPassword == null)
          stg.userPassword = OUser.ADMIN;

        int idx = stg.path.indexOf(':');
        if (idx == -1) {
          OLogManager.instance().error(this, "-> Invalid path '" + stg.path + "' for database '" + stg.name + "'");
          return;
        }
        type = stg.path.substring(0, idx);

        ODatabaseDocument db = null;
        try {
          db = new ODatabaseDocumentTx(stg.path);

          if (db.exists())
            db.open(stg.userName, stg.userPassword);
          else {
            db.create();
            if (stg.userName.equals(OUser.ADMIN)) {
              if (!stg.userPassword.equals(OUser.ADMIN))
                // CHANGE ADMIN PASSWORD
                db.getMetadata().getSecurity().getUser(OUser.ADMIN).setPassword(stg.userPassword);
            } else {
              // CREATE A NEW USER AS ADMIN AND REMOVE THE DEFAULT ONE
              db.getMetadata().getSecurity().createUser(stg.userName, stg.userPassword, ORole.ADMIN);
              db.getMetadata().getSecurity().dropUser(OUser.ADMIN);
              db.close();
              db.open(stg.userName, stg.userPassword);
            }
          }

          OLogManager.instance().info(this, "-> Loaded " + type + " database '" + stg.name + "'");
        } catch (Exception e) {
          OLogManager.instance().error(this, "-> Cannot load " + type + " database '" + stg.name + "': " + e);

        } finally {
          if (db != null)
            db.close();
        }
      }
  }

  protected void createDefaultServerUsers() throws IOException {

    if (serverSecurity != null && !serverSecurity.arePasswordsStored())
      return;

    // ORIENTDB_ROOT_PASSWORD ENV OR JVM SETTING
    String rootPassword = OSystemVariableResolver.resolveVariable(ROOT_PASSWORD_VAR);

    if (rootPassword != null) {
      rootPassword = rootPassword.trim();
      if (rootPassword.isEmpty())
        rootPassword = null;
    }

    if (rootPassword == null && !serverCfg.existsUser(OServerConfiguration.DEFAULT_ROOT_USER)) {
      try {
        // WAIT ANY LOG IS PRINTED
        Thread.sleep(1000);
      } catch (InterruptedException e) {
      }

      System.out.println();
      System.out.println();
      System.out.println(OAnsiCode.format("$ANSI{yellow +---------------------------------------------------------------+}"));
      System.out.println(OAnsiCode.format("$ANSI{yellow |                WARNING: FIRST RUN CONFIGURATION               |}"));
      System.out.println(OAnsiCode.format("$ANSI{yellow +---------------------------------------------------------------+}"));
      System.out.println(OAnsiCode.format("$ANSI{yellow | This is the first time the server is running. Please type a   |}"));
      System.out.println(OAnsiCode.format("$ANSI{yellow | password of your choice for the 'root' user or leave it blank |}"));
      System.out.println(OAnsiCode.format("$ANSI{yellow | to auto-generate it.                                          |}"));
      System.out.println(OAnsiCode.format("$ANSI{yellow |                                                               |}"));
      System.out.println(OAnsiCode.format("$ANSI{yellow | To avoid this message set the environment variable or JVM     |}"));
      System.out.println(OAnsiCode.format("$ANSI{yellow | setting ORIENTDB_ROOT_PASSWORD to the root password to use.   |}"));
      System.out.println(OAnsiCode.format("$ANSI{yellow +---------------------------------------------------------------+}"));

      final OConsoleReader console = new ODefaultConsoleReader();

      // ASK FOR PASSWORD + CONFIRM
      do {
        System.out.print(OAnsiCode.format("\n$ANSI{yellow Root password [BLANK=auto generate it]: }"));
        rootPassword = console.readPassword();

        if (rootPassword != null) {
          rootPassword = rootPassword.trim();
          if (rootPassword.isEmpty())
            rootPassword = null;
        }

        if (rootPassword != null) {
          System.out.print(OAnsiCode.format("$ANSI{yellow Please confirm the root password: }"));

          String rootConfirmPassword = console.readPassword();
          if (rootConfirmPassword != null) {
            rootConfirmPassword = rootConfirmPassword.trim();
            if (rootConfirmPassword.isEmpty())
              rootConfirmPassword = null;
          }

          if (!rootPassword.equals(rootConfirmPassword)) {
            System.out.println(OAnsiCode.format(
                "$ANSI{red ERROR: Passwords don't match, please reinsert both of them, or press ENTER to auto generate it}"));
          } else
            // PASSWORDS MATCH
            break;
        }

      } while (rootPassword != null);

    } else
      OLogManager.instance().warn(this, "Found ORIENTDB_ROOT_PASSWORD variable, using this value as root's password", rootPassword);

    if (!serverCfg.existsUser(OServerConfiguration.DEFAULT_ROOT_USER)) {
      addUser(OServerConfiguration.DEFAULT_ROOT_USER, rootPassword, "*");
    }
    if (!serverCfg.existsUser(OServerConfiguration.GUEST_USER)) {
      addUser(OServerConfiguration.GUEST_USER, OServerConfiguration.GUEST_PASS, "connect,server.listDatabases,server.dblist");
    }
  }

  public OServerPluginManager getPluginManager() {
    return pluginManager;
  }

  protected void registerPlugins() throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    pluginManager = new OServerPluginManager();
    pluginManager.config(this);
    pluginManager.startup();

    // PLUGINS CONFIGURED IN XML
    final OServerConfiguration configuration = serverCfg.getConfiguration();

    if (configuration.handlers != null) {
      // ACTIVATE PLUGINS
      OServerPlugin handler;
      for (OServerHandlerConfiguration h : configuration.handlers) {
        if (h.parameters != null) {
          // CHECK IF IT'S ENABLED
          boolean enabled = true;

          for (OServerParameterConfiguration p : h.parameters) {
            if (p.name.equals("enabled")) {
              enabled = false;

              String value = OSystemVariableResolver.resolveSystemVariables(p.value);
              if (value != null) {
                value = value.trim();

                if ("true".equalsIgnoreCase(value)) {
                  enabled = true;
                  break;
                }
              }
            }
          }

          if (!enabled)
            // SKIP IT
            continue;
        }

        handler = (OServerPlugin) loadClass(h.clazz).newInstance();

        if (handler instanceof ODistributedServerManager)
          distributedManager = (ODistributedServerManager) handler;

        pluginManager.registerPlugin(new OServerPluginInfo(handler.getName(), null, null, null, handler, null, 0, null));

        handler.config(this, h.parameters);
        handler.startup();
      }
    }
  }

  protected void defaultSettings() {
  }

  private boolean isStorageOfCurrentServerInstance(OStorage storage) {
    if (storage.getUnderlying() instanceof OLocalPaginatedStorage) {
      final String rootDirectory = getDatabaseDirectory();
      return storage.getURL().contains(rootDirectory);
    } else
      return true;
  }

  private void scanDatabaseDirectory(final File directory, final Map storages) {
    if (directory.exists() && directory.isDirectory()) {
      final File[] files = directory.listFiles();
      if (files != null)
        for (File db : files) {
          if (db.isDirectory()) {
            final File plocalFile = new File(db.getAbsolutePath() + "/database.ocf");
            final String dbPath = db.getPath().replace('\\', '/');
            final int lastBS = dbPath.lastIndexOf('/', dbPath.length() - 1) + 1;// -1 of dbPath may be ended with slash
            if (plocalFile.exists()) {
              storages.put(OIOUtils.getDatabaseNameFromPath(dbPath.substring(lastBS)), "plocal:" + dbPath);
            } else
              // TRY TO GO IN DEEP RECURSIVELY
              scanDatabaseDirectory(db, storages);
          }
        }
    }
  }

  public OTokenHandler getTokenHandler() {
    return tokenHandler;
  }

  private void initSystemDatabase() {
    systemDatabase = new OSystemDatabase(this);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy