Tony CHEMIT pushed to branch develop at ultreiaio / ird-observe Commits: c992ad78 by Tony Chemit at 2022-02-21T09:38:08+01:00 Vérifier comment est utilisé le timeout sur la connexion server - Closes #2169 - - - - - 981f43b2 by Tony Chemit at 2022-02-21T09:55:39+01:00 update pom - - - - - 58a8c0e3 by Tony Chemit at 2022-02-21T12:15:25+01:00 Erreur TopiaQueryException sur service PUT pour modifier une marée existante - Closes ultreiaio/ird-observe#2142 - - - - - 9f60f71c by Tony Chemit at 2022-02-21T14:25:18+01:00 Amélioration de la gestion de la transaction sur un service en erreur - Closes ultreiaio/ird-observe#2172 - - - - - 20 changed files: - core/services/local/src/main/java/fr/ird/observe/services/local/ObserveServiceContextLocal.java - core/services/local/src/main/java/fr/ird/observe/services/local/service/ObserveServiceLocal.java - core/services/local/src/test/java/fr/ird/observe/services/local/service/api/DataEntityServiceLocalWriteTest.java - core/services/test/src/main/java/fr/ird/observe/services/service/api/DataEntityServiceFixtures.java - pom.xml - server/configuration-tools/README.md - server/configuration/src/main/java/fr/ird/observe/server/configuration/ServerConfig.java - server/configuration/src/main/java/fr/ird/observe/server/configuration/ServerResources.java - server/configuration/src/test/resources/log.xml → server/configuration/src/test/resources/META-INF/configuration/log.xml - server/configuration/src/main/resources/defaultSecurity.yml → server/configuration/src/test/resources/META-INF/configuration/security.yml - server/core/src/main/java/fr/ird/observe/server/controller/AdminController.java - server/core/src/main/java/fr/ird/observe/server/security/ObserveWebSecurityApplicationContext.java - server/core/src/main/java/fr/ird/observe/server/security/ObserveWebSecurityConfigCache.java - server/core/src/main/java/fr/ird/observe/server/security/ObserveWebSecurityConfigCacheKey.java → server/core/src/main/java/fr/ird/observe/server/security/ObserveWebSecurityConfigCacheValue.java - server/core/src/main/java/fr/ird/observe/server/security/ObserveWebSecuritySessionCache.java - server/core/src/main/java/fr/ird/observe/server/security/ObserveWebUserSession.java - server/runner/README.md - server/runner/src/main/assembly/server.xml - server/runner/src/main/i18n/translations/server-runner_fr_FR.properties - server/runner/src/main/resources/log.xml → server/runner/src/main/resources/META-INF/configuration/log.xml Changes: ===================================== core/services/local/src/main/java/fr/ird/observe/services/local/ObserveServiceContextLocal.java ===================================== @@ -53,6 +53,11 @@ public final class ObserveServiceContextLocal extends ObserveServicesProviderImp private ObserveTopiaApplicationContext topiaApplicationContext; private DecoratorService decoratorService; + /** + * If any error was found, will stop any commit then closing transaction. + */ + private Throwable error; + ObserveServiceContextLocal(ObserveServiceInitializer serviceInitializer, ObserveServiceFactory serviceFactory) { super(serviceFactory, () -> serviceInitializer); } @@ -143,6 +148,17 @@ public final class ObserveServiceContextLocal extends ObserveServicesProviderImp return persistenceContext; } + public Throwable getError() { + return error; + } + + public void setError(Throwable error, String methodName) { + this.error = Objects.requireNonNull(error); + if (log.isInfoEnabled()) { + log.error(String.format("Could not invoke for %s", methodName), error); + } + } + public void checkCredentials(String methodName, Permission methodeCredentials) { if (methodeCredentials != null && serviceInitializer().withConnection()) { ObserveDataSourceConnection dataSourceConnection = serviceInitializer().getConnection(); @@ -164,7 +180,11 @@ public final class ObserveServiceContextLocal extends ObserveServicesProviderImp } private void commit(ObserveTopiaPersistenceContext persistenceContext) { - log.debug(String.format("Commit persistenceContext for %s", methodName)); - persistenceContext.commit(); + if (error == null) { + log.debug(String.format("Commit persistenceContext for %s", methodName)); + persistenceContext.commit(); + } else { + log.warn(String.format("Skip commit persistenceContext for %s, due to error on call...", methodName)); + } } } ===================================== core/services/local/src/main/java/fr/ird/observe/services/local/service/ObserveServiceLocal.java ===================================== @@ -334,8 +334,6 @@ public abstract class ObserveServiceLocal implements ObserveService, ServiceCont @Override public final ServiceValidationContext createServiceValidationContext(ValidationRequestConfigurationSupport configuration, ValidationRequest request) { -// ObserveServicesProviderImpl servicesProvider = new ObserveServicesProviderImpl(serviceContext.serviceFactory(), serviceContext::serviceInitializer); -// ServiceValidationContext validationContext = new ServiceValidationContext((ValidationRequestConfiguration) configuration, getDecoratorService(), new ProjectSelectModel(), servicesProvider); ServiceValidationContext validationContext = new ServiceValidationContext((ValidationRequestConfiguration) configuration, getDecoratorService(), new Project(), serviceContext); validationContext.init(); return validationContext; @@ -353,12 +351,28 @@ public abstract class ObserveServiceLocal implements ObserveService, ServiceCont return serviceContext.initWriteTransaction(methodName, methodeCredentials); } - protected final void closeTransaction(boolean doClose) { + protected final void recordError(Exception e, String methodName) { + serviceContext.setError(e, methodName); + } + + protected final void closeTransaction(boolean doClose, long t0, String methodName) { if (doClose) { - serviceContext.closeTransaction(); + try { + serviceContext.closeTransaction(); + } finally { + log(t0, "close transaction", methodName); + } } } + protected final void logInvokeMethod(long t0, String methodName) { + log(t0, "invokeMethod", methodName); + } + + protected final void log(long t0, String prefix, String methodName) { + TIME_LOG.log(t0, String.format("%s %s.%s", prefix, getClass().getName(), methodName)); + } + protected final ObserveServiceContextLocal serviceContext() { return serviceContext; } ===================================== core/services/local/src/test/java/fr/ird/observe/services/local/service/api/DataEntityServiceLocalWriteTest.java ===================================== @@ -22,11 +22,11 @@ package fr.ird.observe.services.local.service.api; * #L% */ +import fr.ird.observe.dto.ToolkitId; import fr.ird.observe.dto.data.DataDto; import fr.ird.observe.dto.db.DataSourceValidationMode; import fr.ird.observe.navigation.tree.io.ToolkitTreeNodeStates; import fr.ird.observe.services.service.api.InvalidDataException; -import fr.ird.observe.services.service.data.ps.common.TripService; import fr.ird.observe.test.DatabaseName; import fr.ird.observe.test.spi.CopyDatabaseConfiguration; import fr.ird.observe.test.spi.DatabaseNameConfiguration; @@ -35,6 +35,7 @@ import org.junit.Test; import java.util.List; import java.util.Map; +import java.util.Objects; public class DataEntityServiceLocalWriteTest extends GeneratedDataEntityServiceLocalWriteTest { @Override @@ -69,13 +70,13 @@ public class DataEntityServiceLocalWriteTest extends GeneratedDataEntityServiceL public void createPsTrip() throws InvalidDataException { TOPIA_TEST_CLASS_RESOURCE.getServiceInitializerConfig().setValidationMode(DataSourceValidationMode.NONE); { - List<?> data = assertTrip(TripService.class, - fr.ird.observe.dto.data.ps.common.TripDto.class, "gearUseFeatures", 1); + List<?> data = assertCreateTrip(fr.ird.observe.services.service.data.ps.common.TripService.class, + fr.ird.observe.dto.data.ps.common.TripDto.class, "gearUseFeatures", 1); Assert.assertNotNull(data); } { - List<?> data = assertTrip(TripService.class, - fr.ird.observe.dto.data.ps.common.TripDto.class, "routeObs", 2); + List<?> data = assertCreateTrip(fr.ird.observe.services.service.data.ps.common.TripService.class, + fr.ird.observe.dto.data.ps.common.TripDto.class, "routeObs", 2); Assert.assertNotNull(data); } } @@ -88,13 +89,13 @@ public class DataEntityServiceLocalWriteTest extends GeneratedDataEntityServiceL TOPIA_TEST_CLASS_RESOURCE.getServiceInitializerConfig().setValidationMode(DataSourceValidationMode.NONE); try { { - List<?> data = assertTrip(TripService.class, - fr.ird.observe.dto.data.ps.common.TripDto.class, "routeLogbook", 1); + List<?> data = assertCreateTrip(fr.ird.observe.services.service.data.ps.common.TripService.class, + fr.ird.observe.dto.data.ps.common.TripDto.class, "routeLogbook", 1); Assert.assertNotNull(data); } { - Map<String, Object> data = assertTrip0(TripService.class, - fr.ird.observe.dto.data.ps.common.TripDto.class, "all"); + Map<String, Object> data = assertCreateTrip0(fr.ird.observe.services.service.data.ps.common.TripService.class, + fr.ird.observe.dto.data.ps.common.TripDto.class, "all"); Assert.assertNotNull(data); } } finally { @@ -102,28 +103,55 @@ public class DataEntityServiceLocalWriteTest extends GeneratedDataEntityServiceL } } + @Test + @CopyDatabaseConfiguration + @DatabaseNameConfiguration(DatabaseName.referential) + public void updatePsTrip() throws InvalidDataException { + TOPIA_TEST_CLASS_RESOURCE.getServiceInitializerConfig().setValidationMode(DataSourceValidationMode.NONE); + Map<?, ?> tripMap = null; + { + tripMap = assertCreateTrip0(fr.ird.observe.services.service.data.ps.common.TripService.class, + fr.ird.observe.dto.data.ps.common.TripDto.class, "gearUseFeatures"); + Assert.assertNotNull(tripMap); + } + Objects.requireNonNull(tripMap); + String tripId = (String) tripMap.get(ToolkitId.PROPERTY_TOOLKIT_TOPIA_ID); + int exceptedRouteCount = 2; + { + List<?> data = assertUpdateTrip(fr.ird.observe.services.service.data.ps.common.TripService.class, + fr.ird.observe.dto.data.ps.common.TripDto.class, Objects.requireNonNull(tripId), "routeObs", exceptedRouteCount); + Assert.assertNotNull(data); + } + { + List<?> data = assertUpdateTrip(fr.ird.observe.services.service.data.ps.common.TripService.class, + fr.ird.observe.dto.data.ps.common.TripDto.class, Objects.requireNonNull(tripId), "routeObs", 2*exceptedRouteCount); + Assert.assertNotNull(data); + } + //FIXME Add more test for update https://gitlab.com/ultreiaio/ird-observe/-/issues/2171 + } + @Test @CopyDatabaseConfiguration public void createLlTrip() throws InvalidDataException { TOPIA_TEST_CLASS_RESOURCE.getServiceInitializerConfig().setValidationMode(DataSourceValidationMode.NONE); { - List<?> data = assertTrip(fr.ird.observe.services.service.data.ll.common.TripService.class, - fr.ird.observe.dto.data.ll.common.TripDto.class, "gearUseFeatures", 1); + List<?> data = assertCreateTrip(fr.ird.observe.services.service.data.ll.common.TripService.class, + fr.ird.observe.dto.data.ll.common.TripDto.class, "gearUseFeatures", 1); Assert.assertNotNull(data); } { - List<?> data = assertTrip(fr.ird.observe.services.service.data.ll.common.TripService.class, - fr.ird.observe.dto.data.ll.common.TripDto.class, "activityObs", 2); + List<?> data = assertCreateTrip(fr.ird.observe.services.service.data.ll.common.TripService.class, + fr.ird.observe.dto.data.ll.common.TripDto.class, "activityObs", 2); Assert.assertNotNull(data); } { - List<?> data = assertTrip(fr.ird.observe.services.service.data.ll.common.TripService.class, - fr.ird.observe.dto.data.ll.common.TripDto.class, "activityLogbook", 2); + List<?> data = assertCreateTrip(fr.ird.observe.services.service.data.ll.common.TripService.class, + fr.ird.observe.dto.data.ll.common.TripDto.class, "activityLogbook", 2); Assert.assertNotNull(data); } { - Map<String, Object> data = assertTrip0(fr.ird.observe.services.service.data.ll.common.TripService.class, - fr.ird.observe.dto.data.ll.common.TripDto.class, "all"); + Map<String, Object> data = assertCreateTrip0(fr.ird.observe.services.service.data.ll.common.TripService.class, + fr.ird.observe.dto.data.ll.common.TripDto.class, "all"); Assert.assertNotNull(data); } } @@ -132,8 +160,8 @@ public class DataEntityServiceLocalWriteTest extends GeneratedDataEntityServiceL @CopyDatabaseConfiguration public void createPsTripLinks() throws InvalidDataException { TOPIA_TEST_CLASS_RESOURCE.getServiceInitializerConfig().setValidationMode(DataSourceValidationMode.NONE); - Map<String, Object> data = assertTrip0(TripService.class, - fr.ird.observe.dto.data.ps.common.TripDto.class, "links"); + Map<String, Object> data = assertCreateTrip0(fr.ird.observe.services.service.data.ps.common.TripService.class, + fr.ird.observe.dto.data.ps.common.TripDto.class, "links"); Assert.assertNotNull(data); } @@ -141,13 +169,13 @@ public class DataEntityServiceLocalWriteTest extends GeneratedDataEntityServiceL @CopyDatabaseConfiguration public void createLlTripLinks() throws InvalidDataException { TOPIA_TEST_CLASS_RESOURCE.getServiceInitializerConfig().setValidationMode(DataSourceValidationMode.NONE); - Map<String, Object> data = assertTrip0(fr.ird.observe.services.service.data.ll.common.TripService.class, - fr.ird.observe.dto.data.ll.common.TripDto.class, "links"); + Map<String, Object> data = assertCreateTrip0(fr.ird.observe.services.service.data.ll.common.TripService.class, + fr.ird.observe.dto.data.ll.common.TripDto.class, "links"); Assert.assertNotNull(data); } - protected List<?> assertTrip(Class<?> serviceType, Class<? extends DataDto> dtoType, String classifier, int expectedCount) throws InvalidDataException { - Map<String, Object> resultObject = assertTrip0(serviceType, dtoType, classifier); + protected List<?> assertCreateTrip(Class<?> serviceType, Class<? extends DataDto> dtoType, String classifier, int expectedCount) throws InvalidDataException { + Map<String, Object> resultObject = assertCreateTrip0(serviceType, dtoType, classifier); Assert.assertNotNull(resultObject); List<?> data = (List<?>) resultObject.get(classifier); @@ -156,7 +184,8 @@ public class DataEntityServiceLocalWriteTest extends GeneratedDataEntityServiceL return data; } - protected Map<String, Object> assertTrip0(Class<?> serviceType, Class<? extends DataDto> dtoType, String classifier) throws InvalidDataException { + + protected Map<String, Object> assertCreateTrip0(Class<?> serviceType, Class<? extends DataDto> dtoType, String classifier) throws InvalidDataException { String json = fixtures.loadContent(serviceType, classifier); ToolkitTreeNodeStates result = fixtures.testCreate(service, dtoType, json); String content = result.getState("content"); @@ -166,4 +195,25 @@ public class DataEntityServiceLocalWriteTest extends GeneratedDataEntityServiceL return resultObject; } + + protected List<?> assertUpdateTrip(Class<?> serviceType, Class<? extends DataDto> dtoType, String id, String classifier, int expectedCount) throws InvalidDataException { + Map<String, Object> resultObject = assertUpdateTrip0(serviceType, dtoType, id, classifier); + Assert.assertNotNull(resultObject); + + List<?> data = (List<?>) resultObject.get(classifier); + Assert.assertNotNull(data); + Assert.assertEquals(expectedCount, data.size()); + return data; + } + + protected Map<String, Object> assertUpdateTrip0(Class<?> serviceType, Class<? extends DataDto> dtoType, String id, String classifier) throws InvalidDataException { + String json = fixtures.loadContent(serviceType, classifier); + ToolkitTreeNodeStates result = fixtures.testUpdate(service, dtoType, id, json); + String content = result.getState("content"); + Assert.assertNotNull(content); + @SuppressWarnings("unchecked") Map<String, Object> resultObject = (Map<String, Object>) fixtures.fromJsonList(content).get(0); + Assert.assertNotNull(resultObject); + return resultObject; + + } } ===================================== core/services/test/src/main/java/fr/ird/observe/services/service/api/DataEntityServiceFixtures.java ===================================== @@ -378,7 +378,7 @@ public class DataEntityServiceFixtures extends GeneratedDataEntityServiceFixture } try { String content = loadFixture(module, subModule, dtoType, "content.json"); - testUpdate(service, dtoType, content); + testUpdate(service, dtoType, null, content); } catch (IOException e) { log.warn(String.format("No fixture for %s", dtoType)); } catch (InvalidDataException e) { @@ -404,11 +404,14 @@ public class DataEntityServiceFixtures extends GeneratedDataEntityServiceFixture return getResult; } - public ToolkitTreeNodeStates testUpdate(DataEntityService service, Class<? extends DataDto> dtoType, String content) throws InvalidDataException { + public ToolkitTreeNodeStates testUpdate(DataEntityService service, Class<? extends DataDto> dtoType, String id, String content) throws InvalidDataException { log.debug(String.format("for type: %s", dtoType.getName())); Pair<String, List<Object>> result = JsonHelper.removeAndCollectProperties(gson, content, ToolkitId.PROPERTY_TOOLKIT_TOPIA_ID, ToolkitId.PROPERTY_TOOLKIT_CREATE_DATE, ToolkitId.PROPERTY_TOOLKIT_LAST_UPDATE_DATE); content = result.getKey(); - String id = (String) result.getValue().get(0); + if (id==null) { + + id = (String) result.getValue().get(0); + } ToolkitId actual = service.update(dtoType, Objects.requireNonNull(id), content); Assert.assertNotNull(actual); Assert.assertEquals(id, actual.getTopiaId()); ===================================== pom.xml ===================================== @@ -23,7 +23,7 @@ <parent> <groupId>io.ultreia.maven</groupId> <artifactId>pom</artifactId> - <version>2022.21</version> + <version>2022.22</version> </parent> <groupId>fr.ird.observe</groupId> <artifactId>ird-observe</artifactId> @@ -155,7 +155,7 @@ <!-- build timestamp configuration --> <maven.build.timestamp.format>dd/MM/yyyy HH:mm z</maven.build.timestamp.format> <buildDate>${maven.build.timestamp}</buildDate> - <lib.version.toolkit>6.0.2</lib.version.toolkit> + <lib.version.toolkit>6.0.3</lib.version.toolkit> <lib.version.ognl>3.1.29</lib.version.ognl> <!--can't use 1.4.197 (date has changed + blob also)--> <lib.version.h2>1.4.196</lib.version.h2> ===================================== server/configuration-tools/README.md ===================================== @@ -29,7 +29,7 @@ sh default-migrate-server-v7.sh ## Optimize v9 server configuration file -v9 **security.yml** file can be optimize. +v9 **security.yml** file can be optimized. Run the script **optimize-server-v9.sh** for this purpose. ===================================== server/configuration/src/main/java/fr/ird/observe/server/configuration/ServerConfig.java ===================================== @@ -90,7 +90,7 @@ public class ServerConfig extends GeneratedServerConfig implements CleanTemporar fakeConfig.initFirst(); // Now that common files are ready, starts a normal configuration without system file and without migration possible - ApplicationConfigInit realInit = ApplicationConfigInit.forAllScopesWithout(ApplicationConfigScope.HOME, ApplicationConfigScope.ENV, ApplicationConfigScope.SYSTEM) + ApplicationConfigInit realInit = ApplicationConfigInit.forAllScopesWithout(ApplicationConfigScope.HOME, ApplicationConfigScope.ENV, ApplicationConfigScope.SYSTEM) .addDefaults(ServerConfigOption.CONTEXT_PATH.getKey(), contextPath); ServerConfig config = new ServerConfig(setInstanceExtraConfigDirectory(setConfigFileName(realInit))); @@ -133,22 +133,22 @@ public class ServerConfig extends GeneratedServerConfig implements CleanTemporar } public void initFirst() { - log.info("Starts to init ObServe fake server configuration (to generate default directories and common files..."); + log.info("Starts to init ObServe fake server configuration (to generate default directories and common files...)"); parse(); Path baseDirectory = getCommonConfigDirectory().toPath(); - createDirectories(baseDirectory, "Impossible de créer le répertoire principal de l'application (%s)"); + createDirectories(baseDirectory, "Could not create application main directory (%s)"); File extraConfigFile = get().getExtraConfigFile(); if (Files.notExists(extraConfigFile.toPath())) { log.info(String.format("Save common configuration file to: %s", extraConfigFile)); - ConfigHelper.save(get(), extraConfigFile, new String[0], ServerResources.CONFIG, options()); + ConfigHelper.save(get(), extraConfigFile, new String[0], ServerResources.APPLICATION_CONFIGURATION, options()); } File log4jConfigurationFile = getCommonLog4jConfigurationFile(); if (!log4jConfigurationFile.exists()) { - ServerResources.LOG_CONFIGURATION_FILE.copyResource(log4jConfigurationFile); + ServerResources.LOG_CONFIGURATION.copyResource(log4jConfigurationFile); log.info(String.format("Generate empty log4j configuration file to: %s", log4jConfigurationFile)); } } @@ -160,11 +160,11 @@ public class ServerConfig extends GeneratedServerConfig implements CleanTemporar Path baseDirectory = getBaseDirectory().toPath(); log.info(getConfigurationDescription()); - createDirectories(baseDirectory, "Impossible de créer le répertoire principal de l'application (%s)"); + createDirectories(baseDirectory, "Could not create application instance directory (%s)"); File extraConfigFile = get().getExtraConfigFile(); if (Files.notExists(extraConfigFile.toPath())) { - boolean generated = ServerResources.CONFIG.copyResource(getCommonConfigurationFile().toPath(), extraConfigFile); + boolean generated = ServerResources.APPLICATION_CONFIGURATION.copyResource(getCommonConfigurationFile().toPath(), extraConfigFile); if (generated) { log.info(String.format("Generate empty configuration file to: %s", extraConfigFile)); } else { @@ -173,7 +173,7 @@ public class ServerConfig extends GeneratedServerConfig implements CleanTemporar } Path temporaryDirectory = getTemporaryDirectory().toPath(); - createDirectories(temporaryDirectory, "Impossible de créer le répertoire temporaire (%s)"); + createDirectories(temporaryDirectory, "Could not create temporary directory (%s)"); File securityConfigurationFile = getSecurityConfigurationFile(); if (!securityConfigurationFile.exists()) { @@ -181,7 +181,7 @@ public class ServerConfig extends GeneratedServerConfig implements CleanTemporar if (strict && !commonSecurityConfigurationFile.exists()) { throw new IllegalStateException(String.format("Can not start application. Could not find security.yml file.\n\nPlease add it to one of this places:\n\t%s\n\t%s", commonSecurityConfigurationFile, securityConfigurationFile)); } - boolean generated = ServerResources.SECURITY.copyResource(commonSecurityConfigurationFile.toPath(), securityConfigurationFile); + boolean generated = ServerResources.SECURITY_CONFIGURATION.setStrict(strict).copyResource(commonSecurityConfigurationFile.toPath(), securityConfigurationFile); if (generated) { log.info("Generate default security.yml"); } else { @@ -192,28 +192,37 @@ public class ServerConfig extends GeneratedServerConfig implements CleanTemporar log.info("ObServe server configuration init done."); } + public ReferentialLocale getReferentialLocale() { + if (referentialLocale == null) { + referentialLocale = ReferentialLocale.valueOf(getDbLocale()); + } + return referentialLocale; + } + + public String getConfigurationContent() throws IOException { + Path path = get().getExtraConfigFile().toPath(); + return Files.readString(path); + } + + public String getSecurityContent() throws IOException { + Path path = getSecurityConfigurationFile().toPath(); + return Files.readString(path); + } + private void parse() { try { get().parse(); } catch (ArgumentsParserException e) { throw new ObserveWebApplicationConfigInitException("could not parse configuration", e); } - } private void initLog() { File logFile = getLog4jConfigurationFile(); log.info(String.format("Chargement du fichier de log : %s", logFile)); - ObserveUtil.loadLogConfiguration(ServerResources.LOG_CONFIGURATION_FILE, getCommonLog4jConfigurationFile().toPath(), logFile.toPath(), this); + ObserveUtil.loadLogConfiguration(ServerResources.LOG_CONFIGURATION, getCommonLog4jConfigurationFile().toPath(), logFile.toPath(), this); log = LogManager.getLogger(ServerConfig.class); log.info(String.format("Configuration des logs chargée depuis le fichier %s", logFile)); } - public ReferentialLocale getReferentialLocale() { - if (referentialLocale == null) { - referentialLocale = ReferentialLocale.valueOf(getDbLocale()); - } - return referentialLocale; - } - } ===================================== server/configuration/src/main/java/fr/ird/observe/server/configuration/ServerResources.java ===================================== @@ -24,6 +24,10 @@ package fr.ird.observe.server.configuration; import io.ultreia.java4all.config.ConfigResource; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; + /** * Created on 07/12/2021. * @@ -31,7 +35,33 @@ import io.ultreia.java4all.config.ConfigResource; * @since 9.0.0 */ public class ServerResources { - public static final ConfigResource CONFIG = new ConfigResource("/META-INF/configuration/observe-server.conf"); - public static final ConfigResource SECURITY = new ConfigResource("/defaultSecurity.yml"); - public static final ConfigResource LOG_CONFIGURATION_FILE = new ConfigResource("/log.xml"); + public static final ConfigResource APPLICATION_CONFIGURATION = new ConfigResource("/META-INF/configuration/observe-server.conf"); + public static final ConfigResource2 SECURITY_CONFIGURATION = new ConfigResource2("/META-INF/configuration/security.yml"); + public static final ConfigResource LOG_CONFIGURATION = new ConfigResource("/META-INF/configuration/log.xml"); + + static class ConfigResource2 extends ConfigResource { + + private boolean strict; + + public ConfigResource2(String location) { + super(location); + } + + public boolean isStrict() { + return strict; + } + + public ConfigResource2 setStrict(boolean strict) { + this.strict = strict; + return this; + } + + @Override + public boolean copyResource(Path sharedFile, File file) { + if (isStrict() && Files.notExists(sharedFile)) { + throw new IllegalStateException(String.format("You are not allowed to copy this resource: %s", this)); + } + return super.copyResource(sharedFile, file); + } + } } ===================================== server/configuration/src/test/resources/log.xml → server/configuration/src/test/resources/META-INF/configuration/log.xml ===================================== ===================================== server/configuration/src/main/resources/defaultSecurity.yml → server/configuration/src/test/resources/META-INF/configuration/security.yml ===================================== ===================================== server/core/src/main/java/fr/ird/observe/server/controller/AdminController.java ===================================== @@ -23,8 +23,6 @@ package fr.ird.observe.server.controller; */ import fr.ird.observe.dto.server.InvalidServerModelException; -import fr.ird.observe.dto.server.ServerModel; -import fr.ird.observe.dto.server.ServerModelHelper; import fr.ird.observe.server.security.ObserveWebSecurityApplicationContext; import fr.ird.observe.server.security.ObserveWebUserSession; import fr.ird.observe.spi.RenderMarkdown; @@ -54,7 +52,7 @@ public class AdminController extends ObserveWebMotionController { public RenderContent configuration() throws IOException { StringBuilder builder = new StringBuilder(); builder.append("\n## Configuration\n"); - String content = getApplicationConfiguration().getConfigurationDescription(); + String content = getApplicationConfiguration().getConfigurationContent(); builder.append(String.format("\n```properties\n%s\n```", content)); return toHtml(RenderMarkdown.renderContent(builder.toString())); } @@ -77,8 +75,7 @@ public class AdminController extends ObserveWebMotionController { builder.append("\n## Fichier de définition du sécurité\n"); File securityConfigurationFile = getApplicationConfiguration().getSecurityConfigurationFile(); builder.append(String.format("\nEmplacement : %s\n", securityConfigurationFile)); - ServerModel securityModel = ServerModelHelper.load(securityConfigurationFile); - String content = ServerModelHelper.toString(securityModel); + String content = getApplicationConfiguration().getSecurityContent(); builder.append(String.format("\n```yaml\n%s\n```", content)); return toHtml(RenderMarkdown.renderContent(builder.toString())); } ===================================== server/core/src/main/java/fr/ird/observe/server/security/ObserveWebSecurityApplicationContext.java ===================================== @@ -26,8 +26,6 @@ import fr.ird.observe.dto.db.DataSourceApiAccess; import fr.ird.observe.dto.db.configuration.ObserveDataSourceConnection; import fr.ird.observe.dto.referential.ReferentialLocale; import fr.ird.observe.dto.server.ServerModel; -import fr.ird.observe.dto.server.security.InvalidApiAccessException; -import fr.ird.observe.dto.server.security.InvalidAuthenticationTokenException; import fr.ird.observe.server.configuration.ServerConfig; import io.ultreia.java4all.util.Version; @@ -38,7 +36,15 @@ import java.util.Collection; import java.util.Locale; /** - * Pour conserver les données applicatives liée à la sécurité (principale le cache des utilisateurs connectés). + * Application security context. + * <p> + * Manages + * + * <ul> + * <li>a cache of configuration ({@link #configurationCache}, to init a new session from an available configuration)</li> + * <li>a cache of session ({@link #authenticateCache} to keep authenticated session)</li> + * </ul> + * * <p> * Created on 30/08/15. * @@ -46,17 +52,14 @@ import java.util.Locale; */ public class ObserveWebSecurityApplicationContext implements Closeable { - /** - * Cache of session. - */ - protected final ObserveWebSecuritySessionCache authenticateCache; - /** * Cache of available configurations. - * - * @see ObserveWebSecurityConfigCache */ protected final ObserveWebSecurityConfigCache configurationCache; + /** + * Cache of session. + */ + protected final ObserveWebSecuritySessionCache authenticateCache; public ObserveWebSecurityApplicationContext(ServerConfig configuration) { this.authenticateCache = new ObserveWebSecuritySessionCache(configuration.getSessionExpirationDelay()); @@ -64,7 +67,7 @@ public class ObserveWebSecurityApplicationContext implements Closeable { } public synchronized void init(Path temporaryDirectory, ServerModel serverModel, Version modelVersion) { - authenticateCache.removeAllSessions(); + authenticateCache.close(); configurationCache.load(serverModel, modelVersion, temporaryDirectory); } @@ -74,8 +77,8 @@ public class ObserveWebSecurityApplicationContext implements Closeable { public ObserveWebUserSession newConfigurationSession(ObserveWebUserSession anonymousSession, String userLogin, String userPassword, String optionalDatabaseName) { Locale locale = anonymousSession.getApplicationLocale(); - ObserveWebSecurityConfigCacheKey configCacheKey = configurationCache.getEntry(locale, userLogin, optionalDatabaseName, userPassword); - return anonymousSession.toConfigurationSession(configCacheKey); + ObserveWebSecurityConfigCacheValue cacheValue = configurationCache.getValue(locale, userLogin, optionalDatabaseName, userPassword); + return anonymousSession.toConfigurationSession(cacheValue); } public ObserveWebUserSession registerAuthenticatedSession(ObserveWebUserSession configurationSession, ObserveDataSourceConnection connection) { @@ -89,14 +92,7 @@ public class ObserveWebSecurityApplicationContext implements Closeable { } public ObserveWebUserSession getSession(Locale locale, String authenticationToken, DataSourceApiAccess requestApiAccess) { - ObserveWebUserSession session = authenticateCache.getSessionIfPresent(authenticationToken); - if (session == null) { - throw new InvalidAuthenticationTokenException(locale, authenticationToken); - } - if (session.rejectApiAccess(requestApiAccess)) { - throw new InvalidApiAccessException(locale, requestApiAccess, session.getUserPermission().getApiAccess()); - } - return session; + return authenticateCache.getSession(locale, authenticationToken, requestApiAccess); } public void invalidateAuthenticationToken(String authenticationToken) { ===================================== server/core/src/main/java/fr/ird/observe/server/security/ObserveWebSecurityConfigCache.java ===================================== @@ -22,7 +22,6 @@ package fr.ird.observe.server.security; * #L% */ -import com.google.common.base.Strings; import fr.ird.observe.dto.db.configuration.ObserveDataSourceConfiguration; import fr.ird.observe.dto.db.configuration.topia.ObserveDataSourceConfigurationTopiaPG; import fr.ird.observe.dto.server.ServerDatabase; @@ -35,6 +34,7 @@ import fr.ird.observe.dto.server.security.UnknownObserveWebUserException; import fr.ird.observe.dto.server.security.UnknownObserveWebUserForDatabaseException; import fr.ird.observe.dto.server.security.UserLoginNotFoundException; import fr.ird.observe.dto.server.security.UserPasswordNotFoundException; +import io.ultreia.java4all.lang.Strings; import io.ultreia.java4all.util.Version; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -48,7 +48,11 @@ import java.util.TreeMap; import java.util.TreeSet; /** - * Cache of security config. + * Cache of security config, build from the {@code security.yml} file. + * <p> + * The method {@link #load(ServerModel, Version, Path)} load once for all the security model in the {@link #cache}. + * <p> + * After that, to get a configuration, use the method {@link #getKey(String, String)}. * <p> * Created on 19/12/2021. * @@ -60,25 +64,32 @@ public class ObserveWebSecurityConfigCache { private static final Logger log = LogManager.getLogger(ObserveWebSecurityConfigCache.class); /** - * Le cache des configurations disponibles pour les couple (utilisateur#base) connus du système. + * Available configurations indexed by the key {@code login--databaseName}, values of configurations. * - * @see #getUserKey(String, String) + * @see #getKey(String, String) to get a cache key + */ + protected final Map<String, ObserveWebSecurityConfigCacheValue> cache; + /** + * Available logins (used to check if login exists) */ - protected final Map<String, ObserveWebSecurityConfigCacheKey> cache; protected final Set<String> availableUserLogin; /** - * Le nom de la base par défaut à utiliser si elle n'est pas spécifiée. + * Default database name (defined in {@code security.yml}), used if non database is given in a request). * * @see ServerModel#getDefaultDatabase() */ protected String defaultDatabaseName; + protected static String getKey(String userLogin, String databaseName) { + return userLogin + "--" + databaseName; + } + public ObserveWebSecurityConfigCache() { this.cache = new TreeMap<>(); this.availableUserLogin = new TreeSet<>(); } - public void load(ServerModel serverModel, Version modelVersion, Path temporaryDirectory) { + protected void load(ServerModel serverModel, Version modelVersion, Path temporaryDirectory) { cache.clear(); availableUserLogin.clear(); @@ -95,7 +106,7 @@ public class ObserveWebSecurityConfigCache { String jdbcUrl = database.getUrl(); String login = role.getLogin(); String password = role.getPassword(); - String userKey = getUserKey(serverUser.getLogin(), database.getName()); + String userKey = getKey(serverUser.getLogin(), database.getName()); // Create DataSourceConfiguration ObserveDataSourceConfiguration configuration = ObserveDataSourceConfigurationTopiaPG.create( @@ -108,17 +119,18 @@ public class ObserveWebSecurityConfigCache { true, modelVersion); configuration.setTemporaryDirectory(temporaryDirectory); + ObserveWebSecurityConfigCacheValue value = new ObserveWebSecurityConfigCacheValue(serverUser, serverUserPermission, configuration); log.info(String.format("Creates data source configuration for userKey %s : %s", userKey, configuration)); - cache.put(userKey, new ObserveWebSecurityConfigCacheKey(serverUser, serverUserPermission, configuration)); + cache.put(userKey, value); } } } - public ObserveWebSecurityConfigCacheKey getEntry(Locale locale, String userLogin, String databaseName, String userPassword) { - if (Strings.isNullOrEmpty(userLogin)) { + protected ObserveWebSecurityConfigCacheValue getValue(Locale locale, String userLogin, String databaseName, String userPassword) { + if (Strings.isEmpty(userLogin)) { throw new UserLoginNotFoundException(locale); } - if (Strings.isNullOrEmpty(userPassword)) { + if (Strings.isEmpty(userPassword)) { throw new UserPasswordNotFoundException(locale); } if (!availableUserLogin.contains(userLogin)) { @@ -127,21 +139,18 @@ public class ObserveWebSecurityConfigCache { if (databaseName == null) { databaseName = defaultDatabaseName; } - String userKey = getUserKey(userLogin, databaseName); - log.info(String.format("Try to find data source configuration for: %s", userKey)); - ObserveWebSecurityConfigCacheKey configCacheKey = cache.get(userKey); - if (configCacheKey == null) { + String cacheKey = getKey(userLogin, databaseName); + log.info(String.format("Try to find data source configuration for: %s", cacheKey)); + ObserveWebSecurityConfigCacheValue cacheValue = cache.get(cacheKey); + if (cacheValue == null) { throw new UnknownObserveWebUserForDatabaseException(locale, databaseName, userLogin); } - ServerUser user = configCacheKey.getServerUser(); + ServerUser user = cacheValue.getServerUser(); + //FIXME In the cache do not keep real password but a hash if (!Objects.equals(user.getPassword(), userPassword)) { throw new BadObserveWebUserPasswordException(locale, userLogin); } - log.info(String.format("Will use database configuration: %s", configCacheKey.getConfiguration())); - return configCacheKey; - } - - public String getUserKey(String userLogin, String databaseName) { - return userLogin + "--" + databaseName; + log.info(String.format("Will use database configuration: %s", cacheValue.getConfiguration())); + return cacheValue; } } ===================================== server/core/src/main/java/fr/ird/observe/server/security/ObserveWebSecurityConfigCacheKey.java → server/core/src/main/java/fr/ird/observe/server/security/ObserveWebSecurityConfigCacheValue.java ===================================== @@ -26,21 +26,34 @@ import fr.ird.observe.dto.db.configuration.ObserveDataSourceConfiguration; import fr.ird.observe.dto.server.ServerUser; import fr.ird.observe.dto.server.ServerUserPermission; +import java.util.Objects; + /** + * Represents a value in the {@link ObserveWebSecurityConfigCache}. + * <p> * Created on 19/12/2021. * * @author Tony Chemit - dev@tchemit.fr * @since 9.0.0 */ -public class ObserveWebSecurityConfigCacheKey { +public class ObserveWebSecurityConfigCacheValue { + /** + * User. + */ private final ServerUser serverUser; + /** + * User permission. + */ private final ServerUserPermission serverUserPermission; + /** + * Data source configuration. + */ private final ObserveDataSourceConfiguration configuration; - ObserveWebSecurityConfigCacheKey(ServerUser serverUser, ServerUserPermission serverUserPermission, ObserveDataSourceConfiguration configuration) { - this.serverUser = serverUser; - this.serverUserPermission = serverUserPermission; - this.configuration = configuration; + ObserveWebSecurityConfigCacheValue(ServerUser serverUser, ServerUserPermission serverUserPermission, ObserveDataSourceConfiguration configuration) { + this.serverUser = Objects.requireNonNull(serverUser); + this.serverUserPermission = Objects.requireNonNull(serverUserPermission); + this.configuration = Objects.requireNonNull(configuration); } public ServerUser getServerUser() { ===================================== server/core/src/main/java/fr/ird/observe/server/security/ObserveWebSecuritySessionCache.java ===================================== @@ -25,14 +25,18 @@ package fr.ird.observe.server.security; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.cache.RemovalListener; +import fr.ird.observe.dto.db.DataSourceApiAccess; +import fr.ird.observe.dto.server.security.InvalidApiAccessException; +import fr.ird.observe.dto.server.security.InvalidAuthenticationTokenException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.io.Closeable; +import java.util.Locale; import java.util.concurrent.TimeUnit; /** - * Cache of use session. + * Cache of user session. * <p> * Created on 30/08/15. * @@ -49,30 +53,37 @@ public class ObserveWebSecuritySessionCache implements Closeable { public ObserveWebSecuritySessionCache(int expireDelay) { this.sessionCache = CacheBuilder.newBuilder() - .expireAfterWrite(expireDelay, TimeUnit.MINUTES) .expireAfterAccess(expireDelay, TimeUnit.MINUTES) - .removalListener((RemovalListener<String, ObserveWebUserSession>) notification -> log.info(String.format("Remove session: %s - %s", notification.getKey(), notification.getValue()))) + .removalListener((RemovalListener<String, ObserveWebUserSession>) notification -> sessionRemoved(notification.getKey())) .build(); } - public ObserveWebUserSession getSessionIfPresent(String authenticationToken) { - return sessionCache.getIfPresent(authenticationToken); + public ObserveWebUserSession getSession(Locale locale, String authenticationToken, DataSourceApiAccess requestApiAccess) { + ObserveWebUserSession session = sessionCache.getIfPresent(authenticationToken); + if (session == null) { + throw new InvalidAuthenticationTokenException(locale, authenticationToken); + } + if (session.rejectApiAccess(requestApiAccess)) { + throw new InvalidApiAccessException(locale, requestApiAccess, session.getUserPermission().getApiAccess()); + } + return session; + } public void registerSession(ObserveWebUserSession session) { - String sessionId = session.getAuthenticationToken(); - log.info(String.format("Add session: %s for data source configuration: %s", sessionId, session.getConfiguration())); - sessionCache.put(sessionId, session); + String authenticationToken = session.getAuthenticationToken(); + log.info(String.format("Add session: %s for data source configuration: %s", authenticationToken, session.getConfiguration())); + sessionCache.put(authenticationToken, session); } - public void removeSession(String sessionId) { - log.info(String.format("Remove session: %s ", sessionId)); - sessionCache.invalidate(sessionId); + public void removeSession(String authenticationToken) { + log.info(String.format("Will remove session: %s ", authenticationToken)); + sessionCache.invalidate(authenticationToken); + sessionRemoved(authenticationToken); } - public void removeAllSessions() { - log.info("Remove all session"); - sessionCache.invalidateAll(); + public void sessionRemoved(String authenticationToken) { + log.info(String.format("Session removed: %s ", authenticationToken)); } public Cache<String, ObserveWebUserSession> getSessionCache() { @@ -81,7 +92,8 @@ public class ObserveWebSecuritySessionCache implements Closeable { @Override public void close() { - removeAllSessions(); + log.info("Remove all session"); + sessionCache.invalidateAll(); } } ===================================== server/core/src/main/java/fr/ird/observe/server/security/ObserveWebUserSession.java ===================================== @@ -186,7 +186,7 @@ public class ObserveWebUserSession implements ObserveDataSourceConfigurationAndC return new ObserveWebRequestContext(applicationContext, this, serviceInitializerConfig); } - public ObserveWebUserSession toConfigurationSession(ObserveWebSecurityConfigCacheKey configCacheKey) { + public ObserveWebUserSession toConfigurationSession(ObserveWebSecurityConfigCacheValue configCacheKey) { return new ObserveWebUserSession(this, configCacheKey.getConfiguration(), configCacheKey.getServerUser(), configCacheKey.getServerUserPermission()); } ===================================== server/runner/README.md ===================================== @@ -1,2 +1,16 @@ -To deploy new version of pom: mvn deploy -To install localy: mvn install +Welcome to Observe server + +## Init root directory + +By default, server use a special directory to store all his data: ```/var/local/observe-server``` + +The user that run the tomcat instance must be able to read and write in this directory. + +Please apply the following command with root user, before starting the first instance of v9. + +```sh +mkdir -p /var/local/observe-server +chown -R tomcat:staff /var/local/observe-server +``` + +TODO Write a nice document for any help with the server ===================================== server/runner/src/main/assembly/server.xml ===================================== @@ -32,7 +32,7 @@ </includes> </fileSet> <fileSet> - <directory>src/main/resources</directory> + <directory>src/main/resources/META-INF/configuration</directory> <outputDirectory>config</outputDirectory> <includes> <include>log.xml</include> @@ -53,6 +53,14 @@ <include>${applicationName}-${project.version}.war</include> </includes> </fileSet> + <fileSet> + <directory/> + <outputDirectory/> + <fileMode>0755</fileMode> + <includes> + <include>README.md</include> + </includes> + </fileSet> <fileSet> <directory>target/classes</directory> <outputDirectory/> ===================================== server/runner/src/main/i18n/translations/server-runner_fr_FR.properties ===================================== @@ -1,6 +1,6 @@ server.config.name=Observe web Configuration server.config.option.common.directory.config=Répertoire des configurations partagées -server.config.option.common.directory.config.file=Chemin vers le fichier commun de configuration +server.config.option.common.directory.config.file=Chemin vers le fichier commun de configuration de l'application server.config.option.common.directory.config.file.log=Chemin vers le fichier commun de configuration des logs server.config.option.common.directory.config.file.server=Chemin vers le fichier commun de configuration des bases server.config.option.common.directory.instances=Chemin des instances @@ -16,5 +16,5 @@ server.config.option.instance.locale.referential=La langue du référentiel (fr_ server.config.option.instance.security.key=Clé API Admin (À changer) server.config.option.instance.session.maximum.size=Taille maximum de session server.config.option.instance.timeout.http=Temps maximum de tentative de connection http (en millisecondes) -server.config.option.instance.timeout.session=Temps maximum d'une session (en minutes) +server.config.option.instance.timeout.session=Durée de vie d'un jeton d'authentification (en minutes) server.config.option.instance.timeout.temporary.files=Nettoyage des fichiers temporaires (en heures) ===================================== server/runner/src/main/resources/log.xml → server/runner/src/main/resources/META-INF/configuration/log.xml ===================================== View it on GitLab: https://gitlab.com/ultreiaio/ird-observe/-/compare/f99b81531877e69dcbd2e11f5... -- View it on GitLab: https://gitlab.com/ultreiaio/ird-observe/-/compare/f99b81531877e69dcbd2e11f5... You're receiving this email because of your account on gitlab.com.