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 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(); 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> 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 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> schemaToBeCreated = new ArrayList<>(); schemaToBeCreated.add(Property.class); schemaToBeCreated.add(Metadata.class); for(Class clazz : schemaToBeCreated) { ElementManagement erManagement = new PropertyTypeDefinitionManagement(); erManagement.setJson(TypeMapper.serializeType(clazz)); erManagement.create(); } List packages = new ArrayList(); Class typeClz = Type.class; packages.add(typeClz.getPackage()); // Adding all packages of AccessType AccessType[] accessTypes = AccessType.values(); for(AccessType accessType : accessTypes) { Class clazz = accessType.getTypeClass(); packages.add(clazz.getPackage()); } ServiceLoader 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 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 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; } }