From b995d0d8af0144d918f714a2b1d3affca37b73bb Mon Sep 17 00:00:00 2001
From: TCHERNIATINSKY <philippe.tcherniatinsky@inrae.fr>
Date: Tue, 12 Apr 2022 12:11:14 +0200
Subject: [PATCH] Correction du cas d'un authorizationscope
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- si authorizationscope ne pointe pas vers une variablecomponent lié à un référentiel, une erreur est envoyer. Ce référentiel doit fournir une liste de valeurs qui seront autorisées pour cet authorizationscope.

- si ce référentiel existe, mais n'est pas mentionné comme faisant partie d'une compositereference, alors on crée la composite référence "default_0" avec cette référence.
---
 .../rest/ApplicationConfigurationService.java | 216 +++++++++---------
 .../oresing/rest/AuthorizationService.java    |   2 +-
 .../rest/ConfigurationParsingResult.java      |  16 +-
 .../fr/inra/oresing/rest/OreSiResources.java  |  25 +-
 .../ApplicationConfigurationServiceTest.java  |  20 +-
 .../java/fr/inra/oresing/rest/Fixtures.java   |  22 ++
 .../inra/oresing/rest/OreSiResourcesTest.java |  73 ++++++
 .../progressiveyaml/data/date_de_visite.csv   |   8 +
 .../references/agroecosystem.csv              |   4 +
 .../progressiveyaml/references/parcelles.csv  |   5 +
 .../data/progressiveyaml/references/sites.csv |   5 +
 ...thReferenceAndNoHierarchicalReference.yaml |  82 +++++++
 ...estAuthorizationScopeWithoutReference.yaml |  78 +++++++
 ui/src/locales/en.json                        |   3 +-
 ui/src/locales/fr.json                        |   3 +-
 15 files changed, 433 insertions(+), 129 deletions(-)
 create mode 100644 src/test/resources/data/progressiveyaml/data/date_de_visite.csv
 create mode 100644 src/test/resources/data/progressiveyaml/references/agroecosystem.csv
 create mode 100644 src/test/resources/data/progressiveyaml/references/parcelles.csv
 create mode 100644 src/test/resources/data/progressiveyaml/references/sites.csv
 create mode 100644 src/test/resources/data/progressiveyaml/testAuthorizationScopeWithReferenceAndNoHierarchicalReference.yaml
 create mode 100644 src/test/resources/data/progressiveyaml/testAuthorizationScopeWithoutReference.yaml

diff --git a/src/main/java/fr/inra/oresing/rest/ApplicationConfigurationService.java b/src/main/java/fr/inra/oresing/rest/ApplicationConfigurationService.java
index 0764bc737..ffa806ed7 100644
--- a/src/main/java/fr/inra/oresing/rest/ApplicationConfigurationService.java
+++ b/src/main/java/fr/inra/oresing/rest/ApplicationConfigurationService.java
@@ -11,17 +11,9 @@ import com.google.common.collect.Multiset;
 import com.google.common.collect.Sets;
 import com.google.common.collect.TreeMultiset;
 import fr.inra.oresing.OreSiTechnicalException;
-import fr.inra.oresing.checker.CheckerTarget;
-import fr.inra.oresing.checker.DateLineChecker;
-import fr.inra.oresing.checker.GroovyConfiguration;
-import fr.inra.oresing.checker.GroovyLineChecker;
-import fr.inra.oresing.checker.RegularExpressionChecker;
+import fr.inra.oresing.checker.*;
 import fr.inra.oresing.groovy.GroovyExpression;
-import fr.inra.oresing.model.Configuration;
-import fr.inra.oresing.model.Duration;
-import fr.inra.oresing.model.LocalDateTimeRange;
-import fr.inra.oresing.model.ReferenceColumn;
-import fr.inra.oresing.model.VariableComponentKey;
+import fr.inra.oresing.model.*;
 import fr.inra.oresing.model.internationalization.InternationalizationDataTypeMap;
 import fr.inra.oresing.model.internationalization.InternationalizationDisplay;
 import fr.inra.oresing.model.internationalization.InternationalizationMap;
@@ -35,15 +27,7 @@ import org.springframework.stereotype.Component;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.io.IOException;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
+import java.util.*;
 import java.util.stream.Collectors;
 
 @Component
@@ -58,7 +42,7 @@ public class ApplicationConfigurationService {
             .add(GroovyLineChecker.NAME)
             .build();
 
-    ConfigurationParsingResult unzipConfiguration(MultipartFile file){
+    ConfigurationParsingResult unzipConfiguration(MultipartFile file) {
         return null;
     }
 
@@ -117,7 +101,7 @@ public class ApplicationConfigurationService {
         }
 
         for (Map.Entry<String, Configuration.ReferenceDescription> referenceEntry : configuration.getReferences().entrySet()) {
-            verifyReferenceKeyColumns( builder, referenceEntry);
+            verifyReferenceKeyColumns(builder, referenceEntry);
             verifyInternationalizedColumnsExists(configuration, builder, referenceEntry);
             verifyInternationalizedColumnsExistsForPattern(configuration, builder, referenceEntry);
             verifyReferenceColumnsDeclarations(builder, referenceEntry, references);
@@ -354,29 +338,43 @@ public class ApplicationConfigurationService {
                             if (!componentsInDescription.containsKey(component)) {
                                 builder.authorizationVariableComponentKeyUnknownComponent(authorizationScopeVariableComponentKey, componentsInDescription.keySet());
                             } else {
-                                Configuration.CheckerDescription authorizationScopeVariableComponentChecker = dataTypeDescription.getData().get(variable).doGetAllComponentDescriptions().get(authorizationScopeVariableComponentKey.getComponent()).getChecker();
-                                if (authorizationScopeVariableComponentChecker == null || !"Reference".equals(authorizationScopeVariableComponentChecker.getName())) {
-                                    builder.authorizationScopeVariableComponentWrongChecker(authorizationScopeVariableComponentKey, "Date");
-                                }
-                                String refType;
-                                Configuration.CheckerConfigurationDescription checkerConfigurationDescription = null;
-                                if (authorizationScopeVariableComponentChecker != null) {
-                                    checkerConfigurationDescription = authorizationScopeVariableComponentChecker.getParams();
-                                }
-                                if (checkerConfigurationDescription == null) {
-                                    builder.authorizationScopeVariableComponentReftypeNull(authorizationScopeVariableComponentKey, configuration.getReferences().keySet());
+                                final Map<String, Configuration.VariableComponentDescription> allComponentDescriptions = dataTypeDescription.getData().get(variable).doGetAllComponentDescriptions();
+                                if (allComponentDescriptions.get(authorizationScopeVariableComponentKey.getComponent()) == null) {
+                                    builder.authorizationScopeMissingReferenceCheckerForAuthorizationScope(authorizationScopeVariableComponentKeyEntry, dataType);
                                 } else {
-                                    refType = checkerConfigurationDescription.getRefType();
-                                    if (refType == null || !configuration.getReferences().containsKey(refType)) {
-                                        builder.authorizationScopeVariableComponentReftypeUnknown(authorizationScopeVariableComponentKey, refType, configuration.getReferences().keySet());
+                                    Configuration.CheckerDescription authorizationScopeVariableComponentChecker = allComponentDescriptions.get(authorizationScopeVariableComponentKey.getComponent()).getChecker();
+                                    if (authorizationScopeVariableComponentChecker == null || !"Reference".equals(authorizationScopeVariableComponentChecker.getName())) {
+                                        builder.authorizationScopeVariableComponentWrongChecker(authorizationScopeVariableComponentKey, "Date");
+                                    }
+                                    String refType;
+                                    Configuration.CheckerConfigurationDescription checkerConfigurationDescription = null;
+                                    if (authorizationScopeVariableComponentChecker != null) {
+                                        checkerConfigurationDescription = authorizationScopeVariableComponentChecker.getParams();
+                                    }
+                                    if (checkerConfigurationDescription == null) {
+                                        builder.authorizationScopeVariableComponentReftypeNull(authorizationScopeVariableComponentKey, configuration.getReferences().keySet());
                                     } else {
-                                        Set<String> compositesReferences = configuration.getCompositeReferences().values().stream()
-                                                .map(Configuration.CompositeReferenceDescription::getComponents)
-                                                .flatMap(List::stream)
-                                                .map(Configuration.CompositeReferenceComponentDescription::getReference)
-                                                .collect(Collectors.toSet());
-                                        if (!compositesReferences.contains(refType)) {
-                                            builder.authorizationScopeVariableComponentReftypeUnknown(dataType, authorizationScopeName, refType, compositesReferences);
+                                        refType = checkerConfigurationDescription.getRefType();
+                                        if (refType == null || !configuration.getReferences().containsKey(refType)) {
+                                            builder.authorizationScopeVariableComponentReftypeUnknown(authorizationScopeVariableComponentKey, refType, configuration.getReferences().keySet());
+                                        } else {
+                                            final LinkedHashMap<String, Configuration.CompositeReferenceDescription> compositeReferences = configuration.getCompositeReferences();
+                                            Set<String> compositesReferences = compositeReferences.values().stream()
+                                                    .map(Configuration.CompositeReferenceDescription::getComponents)
+                                                    .flatMap(List::stream)
+                                                    .map(Configuration.CompositeReferenceComponentDescription::getReference)
+                                                    .collect(Collectors.toSet());
+                                            if (!compositesReferences.contains(refType)) {
+                                                String key = String.format("default_%d", compositeReferences.keySet().stream()
+                                                        .filter(k -> k.startsWith("default_"))
+                                                        .count());
+                                                final Configuration.CompositeReferenceDescription compositeReferenceDescription = new Configuration.CompositeReferenceDescription();
+                                                final Configuration.CompositeReferenceComponentDescription compositeReferenceComponentDescription = new Configuration.CompositeReferenceComponentDescription();
+                                                compositeReferenceComponentDescription.setReference(refType);
+                                                compositeReferenceDescription.setComponents(List.of(compositeReferenceComponentDescription));
+                                                compositeReferences.put(key, compositeReferenceDescription );
+                                                //builder.authorizationScopeVariableComponentReftypeUnknown(dataType, authorizationScopeName, refType, compositesReferences);
+                                            }
                                         }
                                     }
                                 }
@@ -623,31 +621,6 @@ public class ApplicationConfigurationService {
         }
     }
 
-    /**
-     * Pour lancer une validation d'un `checker` déclaré directement sur une donnée (colonne ou variable/composant).
-     */
-    private interface CheckerOnOneTargetValidationContext {
-
-        /**
-         * Si un checker de type 'Reference' est déclaré, l'ensemble des référentiels qui peuvent être utilisé
-         */
-        Set<String> getReferenceCheckerRefTypeParameterValidValues();
-
-        void unknownReferenceForChecker(String refType, Set<String> references);
-
-        void missingReferenceForChecker(Set<String> references);
-
-        void unknownCheckerOnOneTargetName(String checkerName, ImmutableSet<String> validCheckerNames);
-
-        void invalidPatternForDateChecker(String pattern);
-
-        void invalidDurationForDateChecker(String duration);
-
-        void invalidPatternForRegularExpressionChecker(String pattern);
-
-        void illegalCheckerConfigurationParameter(String checkerName, String parameterName);
-    }
-
     private void verifyCheckerOnOneTarget(CheckerOnOneTargetValidationContext builder, Configuration.CheckerDescription checkerDescription) {
         String checkerName = checkerDescription.getName();
         if ("Reference".equals(checkerName)) {
@@ -935,52 +908,12 @@ public class ApplicationConfigurationService {
         }
     }
 
-    /**
-     * Contexte qu'il faut passer pour vérifier qu'une règle de validation dans le YAML est correcte.
-     */
-    private interface LineValidationRuleDescriptionValidationContext {
-
-        /**
-         * Si un checker de type 'Reference' est déclaré, l'ensemble des référentiels qui peuvent être utilisé
-         */
-        Set<String> getReferenceCheckerRefTypeParameterValidValues();
-
-        /**
-         * Si le YAML exprime une règle de validation, l'ensemble des données qu'on peut accepter dans la configuration pour passer ce checker
-         */
-        Set<CheckerTarget> getAcceptableCheckerTargets();
-
-        void missingRequiredExpression(String validationRuleDescriptionEntryKey);
-
-        void illegalGroovyExpression(String validationRuleDescriptionEntryKey, String expression, GroovyExpression.CompilationError compilationError);
-
-        void missingParamColumnReferenceForChecker(String validationRuleDescriptionEntryKey);
-
-        void missingColumnReferenceForChecker(String validationRuleDescriptionEntryKey, String checkerName, Set<CheckerTarget> knownColumns, ImmutableSet<CheckerTarget> missingColumns);
-
-        void unknownCheckerNameForVariableComponentChecker(String validationRuleDescriptionEntryKey, String name, ImmutableSet<String> checkerOnTargetNames);
-
-        void unknownReferenceForChecker(String validationRuleDescriptionEntryKey, String refType, Set<String> references);
-
-        void missingReferenceForChecker(String validationRuleDescriptionEntryKey, Set<String> references);
-
-        void unknownCheckerNameForValidationRule(String validationRuleDescriptionEntryKey, String checkerName, ImmutableSet<String> allCheckerNames);
-
-        void invalidPatternForDateChecker(String validationRuleDescriptionEntryKey, String pattern);
-
-        void invalidDurationForDateChecker(String validationRuleDescriptionEntryKey, String duration);
-
-        void invalidPatternForRegularExpressionChecker(String validationRuleDescriptionEntryKey, String pattern);
-
-        void illegalCheckerConfigurationParameter(String validationRuleDescriptionEntryKey, String checkerName, String parameterName);
-    }
-
     /**
      * Vérifie une règle de validation exprimée dans le YAML.
      *
-     * @param validationContext à fournir selon qu'on soit de valider une règle qui soit déclarée dans un référentiel ou un type de données
+     * @param validationContext                 à fournir selon qu'on soit de valider une règle qui soit déclarée dans un référentiel ou un type de données
      * @param validationRuleDescriptionEntryKey le nom de la règle à valider
-     * @param lineValidationRuleDescription la configuration de la règle à valider
+     * @param lineValidationRuleDescription     la configuration de la règle à valider
      */
     private void verifyLineValidationRuleDescription(LineValidationRuleDescriptionValidationContext validationContext, String validationRuleDescriptionEntryKey, Configuration.LineValidationRuleDescription lineValidationRuleDescription) {
         Configuration.CheckerDescription checker = lineValidationRuleDescription.getChecker();
@@ -1083,6 +1016,71 @@ public class ApplicationConfigurationService {
                 .build();
     }
 
+    /**
+     * Pour lancer une validation d'un `checker` déclaré directement sur une donnée (colonne ou variable/composant).
+     */
+    private interface CheckerOnOneTargetValidationContext {
+
+        /**
+         * Si un checker de type 'Reference' est déclaré, l'ensemble des référentiels qui peuvent être utilisé
+         */
+        Set<String> getReferenceCheckerRefTypeParameterValidValues();
+
+        void unknownReferenceForChecker(String refType, Set<String> references);
+
+        void missingReferenceForChecker(Set<String> references);
+
+        void unknownCheckerOnOneTargetName(String checkerName, ImmutableSet<String> validCheckerNames);
+
+        void invalidPatternForDateChecker(String pattern);
+
+        void invalidDurationForDateChecker(String duration);
+
+        void invalidPatternForRegularExpressionChecker(String pattern);
+
+        void illegalCheckerConfigurationParameter(String checkerName, String parameterName);
+    }
+
+    /**
+     * Contexte qu'il faut passer pour vérifier qu'une règle de validation dans le YAML est correcte.
+     */
+    private interface LineValidationRuleDescriptionValidationContext {
+
+        /**
+         * Si un checker de type 'Reference' est déclaré, l'ensemble des référentiels qui peuvent être utilisé
+         */
+        Set<String> getReferenceCheckerRefTypeParameterValidValues();
+
+        /**
+         * Si le YAML exprime une règle de validation, l'ensemble des données qu'on peut accepter dans la configuration pour passer ce checker
+         */
+        Set<CheckerTarget> getAcceptableCheckerTargets();
+
+        void missingRequiredExpression(String validationRuleDescriptionEntryKey);
+
+        void illegalGroovyExpression(String validationRuleDescriptionEntryKey, String expression, GroovyExpression.CompilationError compilationError);
+
+        void missingParamColumnReferenceForChecker(String validationRuleDescriptionEntryKey);
+
+        void missingColumnReferenceForChecker(String validationRuleDescriptionEntryKey, String checkerName, Set<CheckerTarget> knownColumns, ImmutableSet<CheckerTarget> missingColumns);
+
+        void unknownCheckerNameForVariableComponentChecker(String validationRuleDescriptionEntryKey, String name, ImmutableSet<String> checkerOnTargetNames);
+
+        void unknownReferenceForChecker(String validationRuleDescriptionEntryKey, String refType, Set<String> references);
+
+        void missingReferenceForChecker(String validationRuleDescriptionEntryKey, Set<String> references);
+
+        void unknownCheckerNameForValidationRule(String validationRuleDescriptionEntryKey, String checkerName, ImmutableSet<String> allCheckerNames);
+
+        void invalidPatternForDateChecker(String validationRuleDescriptionEntryKey, String pattern);
+
+        void invalidDurationForDateChecker(String validationRuleDescriptionEntryKey, String duration);
+
+        void invalidPatternForRegularExpressionChecker(String validationRuleDescriptionEntryKey, String pattern);
+
+        void illegalCheckerConfigurationParameter(String validationRuleDescriptionEntryKey, String checkerName, String parameterName);
+    }
+
     @Getter
     @Setter
     @ToString
diff --git a/src/main/java/fr/inra/oresing/rest/AuthorizationService.java b/src/main/java/fr/inra/oresing/rest/AuthorizationService.java
index 7223f5651..42e61886c 100644
--- a/src/main/java/fr/inra/oresing/rest/AuthorizationService.java
+++ b/src/main/java/fr/inra/oresing/rest/AuthorizationService.java
@@ -125,7 +125,7 @@ public class AuthorizationService {
     }
 
 
-    private SqlPolicy toDatatypePolicy(OreSiAuthorization authorization, OreSiRightOnApplicationRole oreSiRightOnApplicationRole, OperationType operation, SqlPolicy.Statement statement){
+    private SqlPolicy toDatatypePolicy(OreSiAuthorization authorization, OreSiRightOnApplicationRole oreSiRightOnApplicationRole, OperationType operation, SqlPolicy.Statement statement) {
         Set<String> usingExpressionElements = new LinkedHashSet<>();
         Application application = repository.application().findApplication(authorization.getApplication());
         SqlSchemaForApplication sqlSchemaForApplication = SqlSchema.forApplication(application);
diff --git a/src/main/java/fr/inra/oresing/rest/ConfigurationParsingResult.java b/src/main/java/fr/inra/oresing/rest/ConfigurationParsingResult.java
index 1ecd4a984..3919b52d9 100644
--- a/src/main/java/fr/inra/oresing/rest/ConfigurationParsingResult.java
+++ b/src/main/java/fr/inra/oresing/rest/ConfigurationParsingResult.java
@@ -9,10 +9,7 @@ import fr.inra.oresing.rest.validationcheckresults.DefaultValidationCheckResult;
 import lombok.Value;
 
 import javax.annotation.Nullable;
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 
 @Value
 public class ConfigurationParsingResult {
@@ -710,5 +707,14 @@ public class ConfigurationParsingResult {
                     "parameterName", parameterName
             ));
         }
+
+        public void authorizationScopeMissingReferenceCheckerForAuthorizationScope(Map.Entry<String, Configuration.AuthorizationScopeDescription> authorizationScopeVariableComponentKeyEntry, String dataType) {
+            recordError("authorizationScopeMissingReferenceCheckerForAuthorizationScope", ImmutableMap.of(
+                    "authorizationScopeName", authorizationScopeVariableComponentKeyEntry.getKey(),
+                    "variable", authorizationScopeVariableComponentKeyEntry.getValue().getVariable(),
+                    "component", authorizationScopeVariableComponentKeyEntry.getValue().getComponent(),
+                    "dataType",dataType
+            ));
+        }
     }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/fr/inra/oresing/rest/OreSiResources.java b/src/main/java/fr/inra/oresing/rest/OreSiResources.java
index e7a8c814c..45802b555 100644
--- a/src/main/java/fr/inra/oresing/rest/OreSiResources.java
+++ b/src/main/java/fr/inra/oresing/rest/OreSiResources.java
@@ -293,16 +293,19 @@ public class OreSiResources {
                 .map(DataRow::getRowId)
                 .collect(Collectors.toSet());
         Map<Ltree, List<ReferenceValue>> requiredreferencesValues = service.getReferenceDisplaysById(service.getApplication(nameOrId), listOfDataIds);
-        for (Map.Entry<String, LineChecker> referenceCheckersByVariableComponentKey : checkedFormatVariableComponents.get(ReferenceLineChecker.class.getSimpleName()).entrySet()) {
-            String variableComponentKey = referenceCheckersByVariableComponentKey.getKey();
-            ReferenceLineChecker referenceLineChecker = (ReferenceLineChecker) referenceCheckersByVariableComponentKey.getValue();
-            for (Map.Entry<Ltree, UUID> ltreeUUIDEntry : referenceLineChecker.getReferenceValues().entrySet()) {
-
-                final ReferenceValue referenceValue = requiredreferencesValues.getOrDefault(ltreeUUIDEntry.getKey(), List.of())
-                        .stream()
-                        .findFirst().orElse(null);
-                if (referenceValue != null) {
-                    checkedFormatVariableComponents.get(ReferenceLineChecker.class.getSimpleName())
+        final Map<String, LineChecker> referenceLineCheckers = checkedFormatVariableComponents.get(ReferenceLineChecker.class.getSimpleName());
+        if(referenceLineCheckers==null) {
+            //TODO on est dans le cas ou aucun checker reference n'est décrit : authorizationscope  n'est pas un referentiel
+        }else {
+            for (Map.Entry<String, LineChecker> referenceCheckersByVariableComponentKey : referenceLineCheckers.entrySet()) {
+                String variableComponentKey = referenceCheckersByVariableComponentKey.getKey();
+                ReferenceLineChecker referenceLineChecker = (ReferenceLineChecker) referenceCheckersByVariableComponentKey.getValue();
+                for (Map.Entry<Ltree, UUID> ltreeUUIDEntry : referenceLineChecker.getReferenceValues().entrySet()) {
+
+                    final ReferenceValue referenceValue = requiredreferencesValues.getOrDefault(ltreeUUIDEntry.getKey(), List.of())
+                            .stream()
+                            .findFirst().orElse(null);
+                    referenceLineCheckers
                             .put(variableComponentKey, new ReferenceLineCheckerDisplay(referenceLineChecker, referenceValue));
                 }
             }
@@ -465,4 +468,4 @@ public class OreSiResources {
     }
 
 
-}
\ No newline at end of file
+}
diff --git a/src/test/java/fr/inra/oresing/rest/ApplicationConfigurationServiceTest.java b/src/test/java/fr/inra/oresing/rest/ApplicationConfigurationServiceTest.java
index 488837ed9..a08892b2e 100644
--- a/src/test/java/fr/inra/oresing/rest/ApplicationConfigurationServiceTest.java
+++ b/src/test/java/fr/inra/oresing/rest/ApplicationConfigurationServiceTest.java
@@ -600,4 +600,22 @@ public class ApplicationConfigurationServiceTest {
         Assert.assertEquals("Date", onlyError.getMessageParams().get("checkerName"));
         Assert.assertEquals("refType", onlyError.getMessageParams().get("parameterName"));
     }
-}
+
+    @Test
+    public void testauthorizationScopeMissingReferenceCheckerForAuthorizationScope() {
+        String toReplace = "checker:\n" +
+                "              name: Reference\n" +
+                "              params:\n" +
+                "                refType: sites";
+        String replacement = "";
+        ConfigurationParsingResult configurationParsingResult = parseYaml(toReplace, replacement);
+        Assert.assertFalse(configurationParsingResult.isValid());
+        ValidationCheckResult onlyError = Iterables.getOnlyElement(configurationParsingResult.getValidationCheckResults());
+        log.debug(onlyError.getMessage());
+        Assert.assertEquals("authorizationScopeMissingReferenceCheckerForAuthorizationScope", onlyError.getMessage());
+        Assert.assertEquals("localization", onlyError.getMessageParams().get("authorizationScopeName"));
+        Assert.assertEquals("site", onlyError.getMessageParams().get("dataType"));
+        Assert.assertEquals("localization", onlyError.getMessageParams().get("variable"));
+        Assert.assertEquals("site", onlyError.getMessageParams().get("component"));
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/fr/inra/oresing/rest/Fixtures.java b/src/test/java/fr/inra/oresing/rest/Fixtures.java
index 8ef5a3b5a..d27aac4bc 100644
--- a/src/test/java/fr/inra/oresing/rest/Fixtures.java
+++ b/src/test/java/fr/inra/oresing/rest/Fixtures.java
@@ -152,6 +152,28 @@ public class Fixtures {
                 "   }", plateforme, projet, site);
     }
 
+    public String getProgressiveYamlWithNoReferenceForAuthorizationScopeApplicationConfigurationResourceName() {
+        return "/data/progressiveyaml/testAuthorizationScopeWithoutReference.yaml";
+    }
+
+    public String getProgressiveYamlWithReferenceAndNoHierarchicalReferenceForAuthorizationScopeApplicationConfigurationResourceName() {
+        return "/data/progressiveyaml/testAuthorizationScopeWithReferenceAndNoHierarchicalReference.yaml";
+    }
+
+    public Map<String, String> getProgressiveYamlReferentielFiles() {
+        Map<String, String> referentielFiles = new LinkedHashMap<>();
+        referentielFiles.put("agroécosystème", "/data/progressiveyaml/references/agroecosystem.csv");
+        referentielFiles.put("sites", "/data/progressiveyaml/references/sites.csv");
+        referentielFiles.put("parcelles", "/data/progressiveyaml/references/parcelles.csv");
+        return referentielFiles;
+    }
+
+    public Map<String, String> getProgressiveYamlDataFiles() {
+        Map<String, String> dataFiles = new LinkedHashMap<>();
+        dataFiles.put("date_de_visite", "/data/progressiveyaml/data/date_de_visite.csv");
+        return dataFiles;
+    }
+
     public String getRecursivityApplicationConfigurationResourceName() {
         return "/data/recursivite/recusivite.yaml";
     }
diff --git a/src/test/java/fr/inra/oresing/rest/OreSiResourcesTest.java b/src/test/java/fr/inra/oresing/rest/OreSiResourcesTest.java
index 304026d5a..6579f9349 100644
--- a/src/test/java/fr/inra/oresing/rest/OreSiResourcesTest.java
+++ b/src/test/java/fr/inra/oresing/rest/OreSiResourcesTest.java
@@ -592,6 +592,79 @@ public class OreSiResourcesTest {
         }
     }
 
+    @Test
+    public void tesProgressiveYamlWithNoReference() throws Exception {
+
+        URL resource = getClass().getResource(fixtures.getProgressiveYamlWithNoReferenceForAuthorizationScopeApplicationConfigurationResourceName());
+        try (InputStream in = Objects.requireNonNull(resource).openStream()) {
+            MockMultipartFile configuration = new MockMultipartFile("file", "progressive.yaml", "text/plain", in);
+            //définition de l'application
+            authenticationService.addUserRightCreateApplication(userId);
+
+            BadApplicationConfigurationException exception = (BadApplicationConfigurationException) mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/progressive")
+                            .file(configuration)
+                            .cookie(authCookie))
+                    .andExpect(status().is4xxClientError())
+                    //.andExpect(jsonPath("$.id", IsNull.notNullValue()))
+                    .andReturn().getResolvedException();
+            final ValidationCheckResult validationCheckResult = exception.getConfigurationParsingResult().getValidationCheckResults()
+                    .get(0);
+            Assert.assertEquals("authorizationScopeMissingReferenceCheckerForAuthorizationScope", validationCheckResult.getMessage());
+            final Map<String, Object> messageParams = validationCheckResult.getMessageParams();
+           Assert.assertEquals("localization", messageParams.get("authorizationScopeName"));
+           Assert.assertEquals("date_de_visite", messageParams.get("dataType"));
+           Assert.assertEquals("agroecosysteme", messageParams.get("component"));
+           Assert.assertEquals("localisation", messageParams.get("variable"));
+        }
+    }
+
+    @Test
+    public void tesProgressiveYaml() throws Exception {
+
+        URL resource = getClass().getResource(fixtures.getProgressiveYamlWithReferenceAndNoHierarchicalReferenceForAuthorizationScopeApplicationConfigurationResourceName());
+        try (InputStream in = Objects.requireNonNull(resource).openStream()) {
+            MockMultipartFile configuration = new MockMultipartFile("file", "progressive.yaml", "text/plain", in);
+            //définition de l'application
+            authenticationService.addUserRightCreateApplication(userId);
+
+            String response = mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/progressive")
+                            .file(configuration)
+                            .cookie(authCookie))
+                    .andExpect(status().isCreated())
+                    .andExpect(jsonPath("$.id", IsNull.notNullValue()))
+                    .andReturn().getResponse().getContentAsString();
+        }
+
+        String response;
+        // Ajout de referentiel
+        for (Map.Entry<String, String> e : fixtures.getProgressiveYamlReferentielFiles().entrySet()) {
+            try (InputStream refStream = getClass().getResourceAsStream(e.getValue())) {
+                MockMultipartFile refFile = new MockMultipartFile("file", e.getValue(), "text/plain", refStream);
+
+                response = mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/progressive/references/{refType}", e.getKey())
+                                .file(refFile)
+                                .cookie(authCookie))
+                        .andExpect(status().isCreated())
+                        .andExpect(jsonPath("$.id", IsNull.notNullValue()))
+                        .andReturn().getResponse().getContentAsString();
+
+                JsonPath.parse(response).read("$.id");
+            }
+        }
+        for (Map.Entry<String, String> e : fixtures.getProgressiveYamlDataFiles().entrySet()) {
+            try (InputStream refStream = getClass().getResourceAsStream(e.getValue())) {
+                MockMultipartFile refFile = new MockMultipartFile("file", e.getValue(), "text/plain", refStream);
+
+                response = mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/progressive/data/{refType}", e.getKey())
+                                .file(refFile)
+                                .cookie(authCookie))
+                        .andExpect(status().isCreated())
+                        .andExpect(jsonPath("$.fileId", IsNull.notNullValue()))
+                        .andReturn().getResponse().getContentAsString();
+            }
+        }
+    }
+
     @Test
     public void testRecursivity() throws Exception {
 
diff --git a/src/test/resources/data/progressiveyaml/data/date_de_visite.csv b/src/test/resources/data/progressiveyaml/data/date_de_visite.csv
new file mode 100644
index 000000000..f2953d53d
--- /dev/null
+++ b/src/test/resources/data/progressiveyaml/data/date_de_visite.csv
@@ -0,0 +1,8 @@
+numero de releve;125
+date;heure;agroecosysteme
+23/06/2005;23:42;agr1
+23/06/2005;23:18;agr2
+27/06/2006;23:42;agr2
+27/06/2006;23:31;agr3
+20/06/2007;23:42;agr1
+20/06/2007;23:45;agr3
\ No newline at end of file
diff --git a/src/test/resources/data/progressiveyaml/references/agroecosystem.csv b/src/test/resources/data/progressiveyaml/references/agroecosystem.csv
new file mode 100644
index 000000000..99cb13120
--- /dev/null
+++ b/src/test/resources/data/progressiveyaml/references/agroecosystem.csv
@@ -0,0 +1,4 @@
+nom
+agr1
+agr2
+agr3
\ No newline at end of file
diff --git a/src/test/resources/data/progressiveyaml/references/parcelles.csv b/src/test/resources/data/progressiveyaml/references/parcelles.csv
new file mode 100644
index 000000000..2265ce3df
--- /dev/null
+++ b/src/test/resources/data/progressiveyaml/references/parcelles.csv
@@ -0,0 +1,5 @@
+nom de la parcelle;site
+par1;site1
+par2;site1
+par3;site2
+par4;site2
\ No newline at end of file
diff --git a/src/test/resources/data/progressiveyaml/references/sites.csv b/src/test/resources/data/progressiveyaml/references/sites.csv
new file mode 100644
index 000000000..83f2effd9
--- /dev/null
+++ b/src/test/resources/data/progressiveyaml/references/sites.csv
@@ -0,0 +1,5 @@
+Agroécosystème;nom du site
+agr1;site1
+agr1;site2
+agr2;site3
+agr3;site4
\ No newline at end of file
diff --git a/src/test/resources/data/progressiveyaml/testAuthorizationScopeWithReferenceAndNoHierarchicalReference.yaml b/src/test/resources/data/progressiveyaml/testAuthorizationScopeWithReferenceAndNoHierarchicalReference.yaml
new file mode 100644
index 000000000..283a319e1
--- /dev/null
+++ b/src/test/resources/data/progressiveyaml/testAuthorizationScopeWithReferenceAndNoHierarchicalReference.yaml
@@ -0,0 +1,82 @@
+version: 0
+
+application:
+  name: Application de test
+  version: 1
+
+references:
+  agroécosystème:
+    keyColumns: [ nom ]
+    columns:
+      nom:
+  sites:
+    keyColumns: [ nom du site ]
+    columns:
+      Agroécosystème:
+      nom du site:
+  parcelles:
+    keyColumns: [ site, nom de la parcelle ]
+    columns:
+      site:
+      nom de la parcelle:
+
+dataTypes:
+  date_de_visite:
+    data:
+      date:
+        components:
+          day:
+            checker:
+              name: Date
+              params:
+                pattern: dd/MM/yyyy
+          time:
+      localisation:
+        components:
+          agroecosysteme:
+            checker:
+              name: Reference
+              params:
+                refType: agroécosystème
+      relevant:
+        components:
+          numero:
+    format:
+      constants:
+        - rowNumber: 1
+          columnNumber: 2
+          boundTo:
+            variable: relevant
+            component: numero
+          exportHeader: relevant_number
+      headerLine: 2
+      firstRowLine: 3
+      columns:
+        - header: date
+          boundTo:
+            variable: date
+            component: day
+        - header: heure
+          boundTo:
+            variable: date
+            component: time
+        - header: agroecosysteme
+          boundTo:
+            variable: localisation
+            component: agroecosysteme
+
+    authorization:
+      authorizationScopes:
+        localization:
+          variable: localisation
+          component: agroecosysteme
+      timeScope:
+        variable: date
+        component: day
+      dataGroups:
+        all:
+          label: "Tout"
+          data:
+            - date
+            - localisation
+            - relevant
\ No newline at end of file
diff --git a/src/test/resources/data/progressiveyaml/testAuthorizationScopeWithoutReference.yaml b/src/test/resources/data/progressiveyaml/testAuthorizationScopeWithoutReference.yaml
new file mode 100644
index 000000000..450fc0ad9
--- /dev/null
+++ b/src/test/resources/data/progressiveyaml/testAuthorizationScopeWithoutReference.yaml
@@ -0,0 +1,78 @@
+version: 0
+
+application:
+  name: Application de test
+  version: 1
+
+references:
+  agroécosystème:
+    keyColumns: [ nom ]
+    columns:
+      nom:
+  sites:
+    keyColumns: [ nom du site ]
+    columns:
+      Agroécosystème:
+      nom du site:
+  parcelles:
+    keyColumns: [ site, nom de la parcelle ]
+    columns:
+      site:
+      nom de la parcelle:
+
+dataTypes:
+  date_de_visite:
+    data:
+      date:
+        components:
+          day:
+            checker:
+              name: Date
+              params:
+                pattern: dd/MM/yyyy
+          time:
+      localisation:
+        components:
+          agroecosysteme:
+      relevant:
+        components:
+          numero:
+    format:
+      constants:
+        - rowNumber: 1
+          columnNumber: 2
+          boundTo:
+            variable: relevant
+            component: numero
+          exportHeader: relevant_number
+      headerLine: 2
+      firstRowLine: 3
+      columns:
+        - header: date
+          boundTo:
+            variable: date
+            component: day
+        - header: heure
+          boundTo:
+            variable: date
+            component: time
+        - header: agroecosysteme
+          boundTo:
+            variable: localisation
+            component: agroecosysteme
+
+    authorization:
+      authorizationScopes:
+        localization:
+          variable: localisation
+          component: agroecosysteme
+      timeScope:
+        variable: date
+        component: day
+      dataGroups:
+        all:
+          label: "Tout"
+          data:
+            - date
+            - localisation
+            - relevant
\ No newline at end of file
diff --git a/ui/src/locales/en.json b/ui/src/locales/en.json
index 09226ab54..e6cb57906 100644
--- a/ui/src/locales/en.json
+++ b/ui/src/locales/en.json
@@ -111,6 +111,7 @@
         "emptyHeader": "The file contains a column with an empty header",
         "headerColumnPatternNotMatching": "Column header pattern not matching. Pattern to match : <code>{expectedHeaderColumnPattern}</code><br/>Actual header : <code>{actualHeaderColumn}</code>",
         "illegalCheckerConfigurationParameterForReferenceColumnChecker": "For reference <code>{referenceToValidate}</code> and column <code>{column}</code>, a checker <code>{checkerName}</code> is declared but it does not accept a <code>{parameterName}</code> parameter",
+        "authorizationScopeMissingReferenceCheckerForAuthorizationScope": "In the {dataType} data type, you define an <i>authorizationScope</i> {authorizationScopeName} that references the <i>component</i> {component} of the <i>variable</i> {variable } . In order to be able to provide a list of values on which to authorize, you must define a <i>checker</i> of type <i>Reference</i> to the <i>variableComponent</i> ..",
         "illegalCheckerConfigurationParameterForValidationRuleInDataType": "For data type <code>{dataType}</code> and validation rule <code>{validationRuleDescriptionEntryKey}</code>, a checker <code>{checkerName}</code> is declared but it does not accept a <code>{parameterName}</code> parameter",
         "illegalCheckerConfigurationParameterForValidationRuleInReference": "For reference <code>{referenceToValidate}</code> and validation rule <code>{validationRuleDescriptionEntryKey}</code>, a checker <code>{checkerName}</code> is declared but it does not accept a <code>{parameterName}</code> parameter",
         "illegalCheckerConfigurationParameterForVariableComponentChecker": "For data type <code>{dataType}</code>, component <code>{component}</code> of variable <code>{datum}</code>, a checker <code>{checkerName}</code> is declared but it does not accept a <code>{parameterName}</code> parameter",
@@ -293,4 +294,4 @@
         "regEx": ".*",
         "star": "*"
     }
-}
+}
\ No newline at end of file
diff --git a/ui/src/locales/fr.json b/ui/src/locales/fr.json
index 64b0c425e..09170cc08 100644
--- a/ui/src/locales/fr.json
+++ b/ui/src/locales/fr.json
@@ -111,6 +111,7 @@
         "emptyHeader": "Le fichier contient un en-tête de colonne vide",
         "headerColumnPatternNotMatching": "Erreur dans le format de l'en-tête de colonne. Format à respecter : <code>{expectedHeaderColumnPattern}</code><br/>En-tête actuel : <code>{actualHeaderColumn}</code>",
         "illegalCheckerConfigurationParameterForReferenceColumnChecker": "Pour le référentiel <code>{referenceToValidate}</code> et la colonne <code>{column}</code>, un contrôle <code>{checkerName}</code> est utilisé mais il n'accepte pas de paramètre <code>{parameterName}</code>",
+        "authorizationScopeMissingReferenceCheckerForAuthorizationScope": "Dans le type de données {dataType}, vous définissez un <i>authorisationScope</i> {authorizationScopeName} qui fais référence au <i>component</i>  {component} de la <i>variable</i>  {variable} . Afin de pouvoir fournir une liste de valeurs sur lesquelles portera l'authorisation, vous devez définir un  <i>checker</i>  de type  <i>Reference</i>   au <i>variableComponent</i> .",
         "illegalCheckerConfigurationParameterForValidationRuleInDataType": "Pour le type de données <code>{dataType}</code> et la règle de validation <code>{validationRuleDescriptionEntryKey}</code>, un contrôle <code>{checkerName}</code> est utilisé mais il n'accepte pas de paramètre <code>{parameterName}</code>",
         "illegalCheckerConfigurationParameterForValidationRuleInReference": "Pour le référentiel <code>{referenceToValidate}</code> et la règle de validation <code>{validationRuleDescriptionEntryKey}</code>, un contrôle <code>{checkerName}</code> est utilisé mais il n'accepte pas de paramètre <code>{parameterName}</code>",
         "illegalCheckerConfigurationParameterForVariableComponentChecker": "Pour le type de données <code>{dataType}</code>, le composant <code>{component}</code> de la variable <code>{datum}</code>, un contrôle <code>{checkerName}</code> est utilisé mais il n'accepte pas de paramètre <code>{parameterName}</code>",
@@ -292,4 +293,4 @@
         "slash": "/",
         "regEx": ".*"
     }
-}
+}
\ No newline at end of file
-- 
GitLab