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

412 lines
17 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.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.UUID;
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.model.reference.properties.Header;
import org.gcube.informationsystem.model.reference.properties.Property;
import org.gcube.informationsystem.resourceregistry.contexts.ContextUtility;
import org.gcube.informationsystem.resourceregistry.contexts.security.AdminSecurityContext;
import org.gcube.informationsystem.resourceregistry.contexts.security.ContextSecurityContext;
import org.gcube.informationsystem.resourceregistry.contexts.security.SchemaSecurityContext;
import org.gcube.informationsystem.resourceregistry.contexts.security.SecurityContext.PermissionMode;
import org.gcube.informationsystem.resourceregistry.instances.base.ElementManagement;
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.gcube.informationsystem.utils.discovery.ElementSpecilizationDiscovery;
import org.gcube.informationsystem.utils.discovery.RegistrationProvider;
import org.gcube.informationsystem.utils.discovery.SchemaAction;
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)
*/
@SuppressWarnings("unchecked")
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";
private static final String DEFAULT_ADMIN_USERNAME;
private static final String DEFAULT_ADMIN_USERNAME_VARNAME = "DEFAULT_ADMIN_USERNAME";
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 DEFAULT_ADMIN_PASSWORD;
private static final String DEFAULT_ADMIN_PASSWORD_VARNAME = "DEFAULT_ADMIN_PASSWORD";
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;
// Used to indicate virtual admin security context
private static final String ADMIN_SECURITY_CONTEXT;
public static final UUID ADMIN_SECURITY_CONTEXT_UUID;
// Used to persist Schemas
private static final String SCHEMA_SECURITY_CONTEXT;
public static final UUID SCHEMA_SECURITY_CONTEXT_UUID;
// Used to Persist Context and their relations
private static final String CONTEXT_SECURITY_CONTEXT;
public static final UUID CONTEXT_SECURITY_CONTEXT_UUID;
public static final Set<String> RESERVED_CONTEX_UUID_STRING;
public static final Set<UUID> RESERVED_CONTEX_UUID;
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 V = "V";
public static final String E = "E";
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_ADMIN_USERNAME = properties.getProperty(DEFAULT_ADMIN_USERNAME_VARNAME);
DEFAULT_ADMIN_PASSWORD = properties.getProperty(DEFAULT_ADMIN_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(Exception e) {
logger.error("Unable to load properties from {}", PROPERTY_FILENAME);
throw new RuntimeException("Unable to load properties", e);
}
RESERVED_CONTEX_UUID_STRING = new HashSet<>();
RESERVED_CONTEX_UUID = new HashSet<>();
ADMIN_SECURITY_CONTEXT = "00000000-0000-0000-0000-000000000000";
RESERVED_CONTEX_UUID_STRING.add(ADMIN_SECURITY_CONTEXT);
ADMIN_SECURITY_CONTEXT_UUID = UUID.fromString(ADMIN_SECURITY_CONTEXT);
RESERVED_CONTEX_UUID.add(ADMIN_SECURITY_CONTEXT_UUID);
// Used to persist Schemas
SCHEMA_SECURITY_CONTEXT = "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee";
RESERVED_CONTEX_UUID_STRING.add(SCHEMA_SECURITY_CONTEXT);
SCHEMA_SECURITY_CONTEXT_UUID = UUID.fromString(SCHEMA_SECURITY_CONTEXT);
RESERVED_CONTEX_UUID.add(SCHEMA_SECURITY_CONTEXT_UUID);
// Used to Persist Context and their relations
CONTEXT_SECURITY_CONTEXT = "ffffffff-ffff-ffff-ffff-ffffffffffff";
RESERVED_CONTEX_UUID_STRING.add(CONTEXT_SECURITY_CONTEXT);
CONTEXT_SECURITY_CONTEXT_UUID = UUID.fromString(CONTEXT_SECURITY_CONTEXT);
RESERVED_CONTEX_UUID.add(CONTEXT_SECURITY_CONTEXT_UUID);
try {
boolean created = initGraphDB();
ContextUtility contextUtility = ContextUtility.getInstance();
AdminSecurityContext adminSecurityContext = new AdminSecurityContext();
contextUtility.addSecurityContext(adminSecurityContext.getUUID().toString(), adminSecurityContext);
ContextSecurityContext contextSecurityContext = new ContextSecurityContext();
contextUtility.addSecurityContext(contextSecurityContext.getUUID().toString(), contextSecurityContext);
SchemaSecurityContext schemaSecurityContext = new SchemaSecurityContext();
contextUtility.addSecurityContext(schemaSecurityContext.getUUID().toString(), schemaSecurityContext);
if(created) {
ODatabasePool pool = new ODatabasePool(DatabaseEnvironment.DB_URI, CHANGED_ADMIN_USERNAME,
CHANGED_ADMIN_PASSWORD);
ODatabaseDocument oDatabaseDocument = pool.acquire();
adminSecurityContext.create(oDatabaseDocument);
oDatabaseDocument.commit();
oDatabaseDocument.close();
pool.close();
contextSecurityContext.create();
schemaSecurityContext.create();
List<Class<? extends Element>> definitionToBeCreated = new ArrayList<>();
definitionToBeCreated.add(PropertyElement.class);
definitionToBeCreated.add(Property.class);
definitionToBeCreated.add(Header.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);
SchemaAction schemaAction = new SchemaActionImpl();
for(Class<? extends Element> clz : definitionToBeCreated) {
if(PropertyElement.class.isAssignableFrom(clz)) {
schemaAction.managePropertyClass((Class<? extends PropertyElement>) clz);
} else if(EntityElement.class.isAssignableFrom(clz)) {
schemaAction.manageEntityClass((Class<? extends EntityElement>) clz);
} else if(RelationElement.class.isAssignableFrom(clz)) {
schemaAction.manageRelationClass((Class<? extends RelationElement<? extends EntityElement, ? extends EntityElement>>) clz);
}
}
List<Class<? extends Element>> schemaToBeCreated = new ArrayList<>();
schemaToBeCreated.add(Property.class);
schemaToBeCreated.add(Header.class);
for(Class<? extends Element> clz : schemaToBeCreated) {
ElementManagement<? extends OElement,?> erManagement = new PropertyTypeDefinitionManagement();
erManagement.setJson(TypeMapper.serializeType(clz));
erManagement.create();
}
List<Package> packages = new ArrayList<Package>();
Class<Type> tdClz = Type.class;
packages.add(tdClz.getPackage());
AccessType[] accessTypes = AccessType.values();
for(AccessType accessType : accessTypes) {
Class<Element> clz = accessType.getTypeClass();
packages.add(clz.getPackage());
}
ServiceLoader<? extends RegistrationProvider> regsitrationProviders = ServiceLoader
.load(RegistrationProvider.class);
for(RegistrationProvider registrationProvider : regsitrationProviders) {
packages.addAll(registrationProvider.getPackagesToRegister());
}
ElementSpecilizationDiscovery.manageISM(schemaAction, packages);
}
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);
}
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);
}
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);
logger.trace("Connecting to newly created database {} as {} with default password", DB_URI,
DEFAULT_ADMIN_USERNAME);
ODatabasePool pool = new ODatabasePool(orientDB, DB, DEFAULT_ADMIN_USERNAME, DEFAULT_ADMIN_PASSWORD);
ODatabaseSession oDatabaseSession = pool.acquire();
DatabaseEnvironment.setDateTimeFormat(oDatabaseSession);
OMetadata oMetadata = oDatabaseSession.getMetadata();
OSecurity oSecurity = oMetadata.getSecurity();
logger.trace("Changing {} password", DEFAULT_ADMIN_USERNAME);
OUser admin = oSecurity.getUser(DEFAULT_ADMIN_USERNAME);
admin.setPassword(CHANGED_ADMIN_PASSWORD);
admin.save();
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();
for(PermissionMode permissionMode : DEFAULT_PASSWORDS.keySet()) {
OUser oUser = oSecurity.getUser(permissionMode.toString());
oUser.setPassword(DEFAULT_PASSWORDS.get(permissionMode));
oUser.save();
logger.trace("Updating password for user {}", permissionMode.toString());
}
logger.trace(
"Setting Record-level Security (see http://orientdb.com/docs/3.0.x/security/Database-Security.html#record-level-security)");
OSchema oSchema = oMetadata.getSchema();
OClass oRestricted = oSchema.getClass(OSecurity.RESTRICTED_CLASSNAME);
OClass v = oSchema.getClass(V);
v.addSuperClass(oRestricted);
OClass e = oSchema.getClass(E);
e.addSuperClass(oRestricted);
oDatabaseSession.commit();
oDatabaseSession.close();
pool.close();
return true;
}
return false;
} finally {
orientDB.close();
}
}
public static Key getDatabaseKey() {
return KEY;
}
}