resource-registry/src/main/java/org/gcube/informationsystem/resourceregistry/dbinitialization/DatabaseEnvironment.java

332 lines
12 KiB
Java

package org.gcube.informationsystem.resourceregistry.dbinitialization;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.security.Key;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.ServiceLoader;
import org.gcube.common.encryption.SymmetricKey;
import org.gcube.informationsystem.base.reference.AccessType;
import org.gcube.informationsystem.base.reference.Element;
import org.gcube.informationsystem.base.reference.entities.EntityElement;
import org.gcube.informationsystem.base.reference.properties.PropertyElement;
import org.gcube.informationsystem.base.reference.relations.RelationElement;
import org.gcube.informationsystem.discovery.Discovery;
import org.gcube.informationsystem.discovery.RegistrationProvider;
import org.gcube.informationsystem.model.reference.properties.Metadata;
import org.gcube.informationsystem.model.reference.properties.Property;
import org.gcube.informationsystem.resourceregistry.api.exceptions.types.SchemaAlreadyPresentException;
import org.gcube.informationsystem.resourceregistry.contexts.ServerContextCache;
import org.gcube.informationsystem.resourceregistry.contexts.security.AdminSecurityContext;
import org.gcube.informationsystem.resourceregistry.contexts.security.ContextSecurityContext;
import org.gcube.informationsystem.resourceregistry.contexts.security.QueryTemplatesSecurityContext;
import org.gcube.informationsystem.resourceregistry.contexts.security.SecurityContext.PermissionMode;
import org.gcube.informationsystem.resourceregistry.contexts.security.TypeSecurityContext;
import org.gcube.informationsystem.resourceregistry.instances.base.ElementManagement;
import org.gcube.informationsystem.resourceregistry.types.TypeManagement;
import org.gcube.informationsystem.resourceregistry.types.properties.PropertyTypeDefinitionManagement;
import org.gcube.informationsystem.types.TypeMapper;
import org.gcube.informationsystem.types.reference.Type;
import org.gcube.informationsystem.types.reference.entities.EntityType;
import org.gcube.informationsystem.types.reference.entities.FacetType;
import org.gcube.informationsystem.types.reference.entities.ResourceType;
import org.gcube.informationsystem.types.reference.properties.LinkedEntity;
import org.gcube.informationsystem.types.reference.properties.PropertyDefinition;
import org.gcube.informationsystem.types.reference.properties.PropertyType;
import org.gcube.informationsystem.types.reference.relations.ConsistsOfType;
import org.gcube.informationsystem.types.reference.relations.IsRelatedToType;
import org.gcube.informationsystem.types.reference.relations.RelationType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.arcadedb.database.Document;
import com.arcadedb.remote.RemoteDatabase;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class DatabaseEnvironment {
private static Logger logger = LoggerFactory.getLogger(DatabaseEnvironment.class);
protected static final String PROPERTY_FILENAME = "config.properties";
public static final String HOST;
private static final String HOST_VARNAME = "HOST";
public static final int PORT;
private static final String PORT_VARNAME = "REMOTE_PROTOCOL";
public static final String DB;
private static final String DB_VARNAME = "DB";
private static final String ROOT_USERNAME;
private static final String ROOT_USERNAME_VARNAME = "ROOT_USERNAME";
private static final String ROOT_PASSWORD;
private static final String ROOT_PASSWORD_VARNAME = "ROOT_PASSWORD";
public static final String DEFAULT_ADMIN_ROLE = "admin";
private static final String ADMIN_USERNAME;
private static final String ADMIN_USERNAME_VARNAME = "ADMIN_USERNAME";
private static final String ADMIN_PASSWORD;
private static final String ADMIN_PASSWORD_VARNAME = "ADMIN_PASSWORD";
private static final String WRITER_USER_PASSWORD;
private static final String WRITER_USER_PASSWORD_VARNAME = "WRITER_USER_PASSWORD";
private static final String READER_USER_PASSWORD;
private static final String READER_USER_PASSWORD_VARNAME = "READER_USER_PASSWORD";
public static final Map<PermissionMode,String> DEFAULT_PASSWORDS;
protected static final String DB_KEY_FILENAME_VARNAME = "DB_KEY_FILENAME";
protected static final String DB_KEY_ALGORITHM_VARNAME = "DB_KEY_ALGORITHM";
private static final Key KEY;
static {
Properties properties = new Properties();
InputStream input = null;
try {
input = DatabaseEnvironment.class.getClassLoader().getResourceAsStream(PROPERTY_FILENAME);
// load a properties file
properties.load(input);
HOST = properties.getProperty(HOST_VARNAME);
PORT = Integer.valueOf(properties.getProperty(PORT_VARNAME));
DB = properties.getProperty(DB_VARNAME);
ROOT_USERNAME = properties.getProperty(ROOT_USERNAME_VARNAME);
ROOT_PASSWORD = properties.getProperty(ROOT_PASSWORD_VARNAME);
ADMIN_USERNAME = properties.getProperty(ADMIN_USERNAME_VARNAME);
ADMIN_PASSWORD = properties.getProperty(ADMIN_PASSWORD_VARNAME);
WRITER_USER_PASSWORD = properties.getProperty(WRITER_USER_PASSWORD_VARNAME);
READER_USER_PASSWORD = properties.getProperty(READER_USER_PASSWORD_VARNAME);
DEFAULT_PASSWORDS = new HashMap<PermissionMode,String>();
DEFAULT_PASSWORDS.put(PermissionMode.WRITER, WRITER_USER_PASSWORD);
DEFAULT_PASSWORDS.put(PermissionMode.READER, READER_USER_PASSWORD);
} catch(Throwable e) {
logger.error("Unable to load properties from {}", PROPERTY_FILENAME);
throw new RuntimeException("Unable to load properties", e);
}
// Used to Persist Context and their relations
try {
logger.info("Connecting as {} to {}:{}/{}", ROOT_USERNAME, HOST, PORT, DB);
RemoteDatabase database = new RemoteDatabase(HOST, PORT, DB, ADMIN_USERNAME, ADMIN_PASSWORD);
boolean created = initGraphDB(database);
if(created) {
AdminSecurityContext.getInstance().create(database);
QueryTemplatesSecurityContext.getInstance().create();
TypeSecurityContext.getInstance().create();
ContextSecurityContext.getInstance().create();
/* We must create only OrientDB types */
List<Class<? extends Element>> definitionToBeCreated = new ArrayList<>();
definitionToBeCreated.add(PropertyElement.class);
definitionToBeCreated.add(Property.class);
definitionToBeCreated.add(Metadata.class);
definitionToBeCreated.add(PropertyDefinition.class);
definitionToBeCreated.add(PropertyType.class);
definitionToBeCreated.add(LinkedEntity.class);
definitionToBeCreated.add(EntityElement.class);
definitionToBeCreated.add(EntityType.class);
definitionToBeCreated.add(FacetType.class);
definitionToBeCreated.add(ResourceType.class);
definitionToBeCreated.add(RelationElement.class);
definitionToBeCreated.add(RelationType.class);
definitionToBeCreated.add(IsRelatedToType.class);
definitionToBeCreated.add(ConsistsOfType.class);
for(Class<? extends Element> clz : definitionToBeCreated) {
TypeManagement typeManagement = new TypeManagement();
typeManagement.setTypeAndTypeName(clz);
if(clz.equals(Property.class) || clz.equals(Metadata.class) ) {
((TypeManagement) typeManagement).setSkipTypeDefinitionCreation(true);
}
try {
typeManagement.create();
} catch(SchemaAlreadyPresentException sape) {
logger.warn("{} already exists. It will be ignored", typeManagement.getTypeName());
} catch(Exception ex) {
logger.error("Error creating schema for {} {}: {}", typeManagement.getType().getAccessType(), typeManagement.getTypeName(), ex.getMessage());
throw ex;
}
}
/*
* We have already created Property and Metadata
* because Metadata is needed to create
* types for internal use (i.e. Context, EntityType).
*
* For Property and Metadata we also need
* to create the instance in TypeSecurityContext
* as we will do for any other Property specialization.
*/
List<Class<? extends Element>> schemaToBeCreated = new ArrayList<>();
schemaToBeCreated.add(Property.class);
schemaToBeCreated.add(Metadata.class);
for(Class<? extends Element> clazz : schemaToBeCreated) {
ElementManagement<? extends Document,?> erManagement = new PropertyTypeDefinitionManagement();
erManagement.setJson(TypeMapper.serializeType(clazz));
erManagement.create();
}
List<Package> packages = new ArrayList<Package>();
Class<Type> typeClz = Type.class;
packages.add(typeClz.getPackage());
// Adding all packages of AccessType
AccessType[] accessTypes = AccessType.values();
for(AccessType accessType : accessTypes) {
Class<Element> clazz = accessType.getTypeClass();
packages.add(clazz.getPackage());
}
ServiceLoader<? extends RegistrationProvider> regsitrationProviders = ServiceLoader
.load(RegistrationProvider.class);
for(RegistrationProvider registrationProvider : regsitrationProviders) {
packages.addAll(registrationProvider.getPackagesToRegister());
}
AccessType[] veryBaseTypes = new AccessType[] {
AccessType.PROPERTY_ELEMENT,
AccessType.ENTITY_ELEMENT,
AccessType.RELATION_ELEMENT
};
for(AccessType accessType : veryBaseTypes) {
Discovery<Element> discovery = new Discovery<>(accessType.getTypeClass());
discovery.addPackages(packages);
CreateTypeAction createTypeAction = new CreateTypeAction();
discovery.addDiscoveredElementActions(createTypeAction);
discovery.discover();
}
}
logger.info("Database Connection has been properly initialized");
} catch(Throwable e) {
logger.error("Error initializing database connection", e);
throw new RuntimeException("Error initializing database connection", e);
}
KEY = initDbKey(properties);
// Initializing ServerContextCache instance
ServerContextCache.getInstance();
}
protected static Key initDbKey(Properties properties) {
try {
logger.trace("Going to get properties required to load DB key");
String keyFileName = properties.getProperty(DB_KEY_FILENAME_VARNAME);
String keyAlgorithm = properties.getProperty(DB_KEY_ALGORITHM_VARNAME);
logger.debug("Trying to load DB key from file with name {} created for algorithm {}", keyFileName,
keyAlgorithm);
URL keyFileURL = DatabaseEnvironment.class.getClassLoader().getResource(keyFileName);
File keyFile = new File(keyFileURL.toURI());
logger.debug("Trying to load DB key from file {} created for algorithm {}", keyFile.getAbsolutePath(),
keyAlgorithm);
Key key = SymmetricKey.loadKeyFromFile(keyFile, keyAlgorithm);
logger.info("DB Key has been properly initialized");
return key;
} catch(Throwable e) {
logger.error("Error loading DB Key", e);
throw new RuntimeException("Error loading DB Key. Unable to continue", e);
}
}
protected static void setDateTimeFormat(RemoteDatabase database) {
// TODO
// oDatabaseDocument.set(ATTRIBUTES.DATETIMEFORMAT, Element.DATETIME_PATTERN);
}
protected static void setRecordLevelSecurity(RemoteDatabase database) {
logger.trace("Setting Record-level Security");
/*
OSchema oSchema = oMetadata.getSchema();
OClass oRestricted = oSchema.getClass(OSecurity.RESTRICTED_CLASSNAME);
OClass v = oSchema.getClass(VERTEX_CLASS_NAME);
v.addSuperClass(oRestricted);
OClass e = oSchema.getClass(EDGE_CLASS_NAME);
e.addSuperClass(oRestricted);
*/
}
private static boolean initGraphDB(RemoteDatabase database) throws Exception {
try {
if(!!database.exists()) {
logger.info("The database {} does not exist. Going to create it.", DB);
database.create();
DatabaseEnvironment.setDateTimeFormat(database);
List<String> databases = new ArrayList<>();
databases.add(DB);
// TODO Create Role
// ORole adminRole = oSecurity.getRole(DEFAULT_ADMIN_ROLE);
// OUser newAdminUser = oSecurity.createUser(CHANGED_ADMIN_USERNAME, CHANGED_ADMIN_PASSWORD, adminRole);
//
logger.trace("Creating new admin named '{}'", ADMIN_USERNAME);
database.createUser(ADMIN_USERNAME, ADMIN_PASSWORD, databases);
setRecordLevelSecurity(database);
database.commit();
return true;
}
return false;
} finally {
database.close();
}
}
public static Key getDatabaseKey() {
return KEY;
}
}