407 lines
16 KiB
Java
407 lines
16 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.orientechnologies.common.log.OLogManager;
|
|
import com.orientechnologies.orient.client.remote.OStorageRemote.CONNECTION_STRATEGY;
|
|
import com.orientechnologies.orient.core.db.ODatabase.ATTRIBUTES;
|
|
import com.orientechnologies.orient.core.db.ODatabasePool;
|
|
import com.orientechnologies.orient.core.db.ODatabaseSession;
|
|
import com.orientechnologies.orient.core.db.ODatabaseType;
|
|
import com.orientechnologies.orient.core.db.OrientDB;
|
|
import com.orientechnologies.orient.core.db.OrientDBConfig;
|
|
import com.orientechnologies.orient.core.db.document.ODatabaseDocument;
|
|
import com.orientechnologies.orient.core.metadata.OMetadata;
|
|
import com.orientechnologies.orient.core.metadata.schema.OClass;
|
|
import com.orientechnologies.orient.core.metadata.schema.OSchema;
|
|
import com.orientechnologies.orient.core.metadata.security.ORole;
|
|
import com.orientechnologies.orient.core.metadata.security.OSecurity;
|
|
import com.orientechnologies.orient.core.metadata.security.OUser;
|
|
import com.orientechnologies.orient.core.record.OElement;
|
|
|
|
/**
|
|
* @author Luca Frosini (ISTI - CNR)
|
|
*/
|
|
public class DatabaseEnvironment {
|
|
|
|
private static Logger logger = LoggerFactory.getLogger(DatabaseEnvironment.class);
|
|
|
|
protected static final String PROPERTY_FILENAME = "config.properties";
|
|
|
|
private static final String HOST_VARNAME = "HOST";
|
|
|
|
private static final String REMOTE_PROTOCOL;
|
|
private static final String REMOTE_PROTOCOL_VARNAME = "REMOTE_PROTOCOL";
|
|
|
|
private 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 CHANGED_ADMIN_USERNAME;
|
|
private static final String CHANGED_ADMIN_USERNAME_VARNAME = "CHANGED_ADMIN_USERNAME";
|
|
|
|
private static final String CHANGED_ADMIN_PASSWORD;
|
|
private static final String CHANGED_ADMIN_PASSWORD_VARNAME = "CHANGED_ADMIN_PASSWORD";
|
|
|
|
private static final String DEFAULT_CREATED_WRITER_USER_PASSWORD;
|
|
private static final String DEFAULT_CREATED_WRITER_USER_PASSWORD_VARNAME = "DEFAULT_CREATED_WRITER_USER_PASSWORD";
|
|
|
|
private static final String DEFAULT_CREATED_READER_USER_PASSWORD;
|
|
private static final String DEFAULT_CREATED_READER_USER_PASSWORD_VARNAME = "DEFAULT_CREATED_READER_USER_PASSWORD";
|
|
|
|
public static final Map<PermissionMode,String> DEFAULT_PASSWORDS;
|
|
|
|
private static final String HOSTS;
|
|
|
|
private static final String SERVER_URI;
|
|
public static final String DB_URI;
|
|
|
|
public static final CONNECTION_STRATEGY CONNECTION_STRATEGY_PARAMETER = CONNECTION_STRATEGY.ROUND_ROBIN_CONNECT;
|
|
|
|
|
|
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;
|
|
|
|
public static final String VERTEX_CLASS_NAME = OClass.VERTEX_CLASS_NAME;
|
|
public static final String EDGE_CLASS_NAME = OClass.EDGE_CLASS_NAME;
|
|
|
|
static {
|
|
Properties properties = new Properties();
|
|
InputStream input = null;
|
|
|
|
try {
|
|
|
|
input = DatabaseEnvironment.class.getClassLoader().getResourceAsStream(PROPERTY_FILENAME);
|
|
|
|
// load a properties file
|
|
properties.load(input);
|
|
|
|
HOSTS = properties.getProperty(HOST_VARNAME);
|
|
|
|
REMOTE_PROTOCOL = properties.getProperty(REMOTE_PROTOCOL_VARNAME);
|
|
|
|
DB = properties.getProperty(DB_VARNAME);
|
|
SERVER_URI = REMOTE_PROTOCOL + HOSTS;
|
|
DB_URI = SERVER_URI + "/" + DB;
|
|
|
|
ROOT_USERNAME = properties.getProperty(ROOT_USERNAME_VARNAME);
|
|
ROOT_PASSWORD = properties.getProperty(ROOT_PASSWORD_VARNAME);
|
|
|
|
String changedAdminUsername = null;
|
|
try {
|
|
changedAdminUsername = properties.getProperty(CHANGED_ADMIN_USERNAME_VARNAME);
|
|
if(changedAdminUsername == null) {
|
|
// To be compliant with old configuration.properties which does not have
|
|
// CHANGED_ADMIN_USERNAME property we use the db name as admin username
|
|
changedAdminUsername = DB;
|
|
}
|
|
} catch(Exception e) {
|
|
// To be compliant with old configuration.properties which does not have
|
|
// CHANGED_ADMIN_USERNAME property we use the db name as admin username
|
|
changedAdminUsername = DB;
|
|
}
|
|
CHANGED_ADMIN_USERNAME = changedAdminUsername;
|
|
|
|
CHANGED_ADMIN_PASSWORD = properties.getProperty(CHANGED_ADMIN_PASSWORD_VARNAME);
|
|
|
|
DEFAULT_CREATED_WRITER_USER_PASSWORD = properties.getProperty(DEFAULT_CREATED_WRITER_USER_PASSWORD_VARNAME);
|
|
DEFAULT_CREATED_READER_USER_PASSWORD = properties.getProperty(DEFAULT_CREATED_READER_USER_PASSWORD_VARNAME);
|
|
|
|
DEFAULT_PASSWORDS = new HashMap<PermissionMode,String>();
|
|
|
|
DEFAULT_PASSWORDS.put(PermissionMode.WRITER, DEFAULT_CREATED_WRITER_USER_PASSWORD);
|
|
DEFAULT_PASSWORDS.put(PermissionMode.READER, DEFAULT_CREATED_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 {
|
|
boolean created = initGraphDB();
|
|
|
|
if(created) {
|
|
ODatabasePool pool = new ODatabasePool(DatabaseEnvironment.DB_URI, CHANGED_ADMIN_USERNAME,
|
|
CHANGED_ADMIN_PASSWORD);
|
|
ODatabaseDocument oDatabaseDocument = pool.acquire();
|
|
AdminSecurityContext.getInstance().create(oDatabaseDocument);
|
|
oDatabaseDocument.commit();
|
|
oDatabaseDocument.close();
|
|
pool.close();
|
|
|
|
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 OElement,?> 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(ODatabaseDocument oDatabaseDocument) {
|
|
oDatabaseDocument.set(ATTRIBUTES.DATETIMEFORMAT, Element.DATETIME_PATTERN);
|
|
}
|
|
|
|
@Deprecated
|
|
// This code must be removed for OrientDB versions higher than 3.2.X
|
|
protected static void setRecordLevelSecurity(OMetadata oMetadata) {
|
|
logger.trace(
|
|
"Setting Record-level Security (see https://orientdb.org/docs/3.2.x/security/Database-Security.html#record-level-security-deprecated-in-v-31)");
|
|
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() throws Exception {
|
|
OLogManager.instance().setWarnEnabled(false);
|
|
OLogManager.instance().setErrorEnabled(false);
|
|
OLogManager.instance().setInfoEnabled(false);
|
|
OLogManager.instance().setDebugEnabled(false);
|
|
|
|
logger.info("Connecting as {} to {}", ROOT_USERNAME, DB_URI);
|
|
OrientDB orientDB = new OrientDB(SERVER_URI, ROOT_USERNAME, ROOT_PASSWORD, OrientDBConfig.defaultConfig());
|
|
try {
|
|
if(!orientDB.exists(DB)) {
|
|
|
|
logger.info("The database {} does not exist. Going to create it.", DB_URI);
|
|
orientDB.create(DB, ODatabaseType.PLOCAL);
|
|
|
|
ODatabasePool pool = new ODatabasePool(orientDB, DB, ROOT_USERNAME, ROOT_PASSWORD);
|
|
ODatabaseSession oDatabaseSession = pool.acquire();
|
|
|
|
DatabaseEnvironment.setDateTimeFormat(oDatabaseSession);
|
|
|
|
OMetadata oMetadata = oDatabaseSession.getMetadata();
|
|
OSecurity oSecurity = oMetadata.getSecurity();
|
|
|
|
|
|
logger.trace("Creating new admin named '{}'", CHANGED_ADMIN_USERNAME);
|
|
ORole adminRole = oSecurity.getRole(DEFAULT_ADMIN_ROLE);
|
|
OUser newAdminUser = oSecurity.createUser(CHANGED_ADMIN_USERNAME, CHANGED_ADMIN_PASSWORD, adminRole);
|
|
newAdminUser.save();
|
|
|
|
DatabaseEnvironment.changeDefaultAdminPassword(oSecurity);
|
|
|
|
setRecordLevelSecurity(oMetadata);
|
|
|
|
oDatabaseSession.commit();
|
|
oDatabaseSession.close();
|
|
|
|
pool.close();
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
} finally {
|
|
orientDB.close();
|
|
}
|
|
}
|
|
|
|
public static Key getDatabaseKey() {
|
|
return KEY;
|
|
}
|
|
|
|
@Deprecated
|
|
// This code must be removed for OrientDB versions higher than 3.2.X
|
|
public static void changeDefaultAdminPassword(OSecurity oSecurity) {
|
|
for(PermissionMode permissionMode : DEFAULT_PASSWORDS.keySet()) {
|
|
try {
|
|
logger.trace("Going to update password for user {}", permissionMode.toString());
|
|
OUser oUser = oSecurity.getUser(permissionMode.toString());
|
|
oUser.setPassword(DEFAULT_PASSWORDS.get(permissionMode));
|
|
oUser.save();
|
|
logger.trace("Updated password for user {}", permissionMode.toString());
|
|
}catch (Exception e) {
|
|
logger.trace("Unable to update password for user {}. {}", permissionMode.toString(), e.getMessage());
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
|
|
try {
|
|
logger.trace("Removing 'admin' user");
|
|
oSecurity.dropUser("admin");
|
|
}catch (Exception e) {
|
|
logger.info("Unable to delete admin user. {}", e.getMessage());
|
|
}
|
|
}
|
|
|
|
}
|