From d1e22de4ac6067ff379f80c02f11212dccd2202a Mon Sep 17 00:00:00 2001
From: TCHERNIATINSKY <philippe.tcherniatinsky@inrae.fr>
Date: Mon, 25 Apr 2022 17:43:07 +0200
Subject: [PATCH 1/3] on verra

---
 README.md                                     |   2 +-
 .../inra/oresing/model/OreSiRoleForUser.java  |  10 ++
 .../java/fr/inra/oresing/model/OreSiUser.java |   6 +-
 .../persistence/AuthenticationService.java    |  63 ++++++++-
 .../JsonTableRepositoryTemplate.java          |   3 +
 .../inra/oresing/persistence/SqlService.java  |   4 +-
 .../oresing/persistence/UserRepository.java   |  63 ++++++++-
 .../persistence/roles/CurrentUserRoles.java   |  19 +++
 .../oresing/persistence/roles/OreSiRole.java  |   6 +-
 .../oresing/rest/AuthorizationResources.java  |  27 +++-
 .../oresing/rest/AuthorizationService.java    | 104 +++++++++++----
 .../inra/oresing/rest/BadRoleException.java   |  19 +++
 .../NotApplicationCreatorRightsException.java |  27 ++++
 .../oresing/rest/NotSuperAdminException.java  |  14 ++
 .../fr/inra/oresing/rest/OreSiService.java    |  69 +++++++---
 .../fr/inra/oresing/rest/OreSiUserResult.java |  21 +++
 .../migration/main/V1__init_schema.sql        |  90 +++++++------
 .../rest/AuthorizationResourcesTest.java      | 123 ++++++++++++++++--
 .../java/fr/inra/oresing/rest/Fixtures.java   |  63 ++++++---
 .../inra/oresing/rest/OreSiResourcesTest.java | 118 +++++++++++------
 20 files changed, 682 insertions(+), 169 deletions(-)
 create mode 100644 src/main/java/fr/inra/oresing/model/OreSiRoleForUser.java
 create mode 100644 src/main/java/fr/inra/oresing/persistence/roles/CurrentUserRoles.java
 create mode 100644 src/main/java/fr/inra/oresing/rest/BadRoleException.java
 create mode 100644 src/main/java/fr/inra/oresing/rest/NotApplicationCreatorRightsException.java
 create mode 100644 src/main/java/fr/inra/oresing/rest/NotSuperAdminException.java
 create mode 100644 src/main/java/fr/inra/oresing/rest/OreSiUserResult.java

diff --git a/README.md b/README.md
index 43e8756f0..f24392c92 100644
--- a/README.md
+++ b/README.md
@@ -93,5 +93,5 @@ Afin d'essayer l'application, il faut pouvoir se connecter. Il faut pour cela cr
 INSERT INTO OreSiUser (id, login, password) values ('5a4dbd41-3fc9-4b3e-b593-a46bc888a7f9'::uuid, 'poussin', '$2a$12$4gAH34ZwgvgQNS0pbR5dGem1Nle0AT/.UwrZWfqtqMiJ0hXeYMvUG');
 DROP ROLE IF EXISTS "5a4dbd41-3fc9-4b3e-b593-a46bc888a7f9";
 CREATE ROLE "5a4dbd41-3fc9-4b3e-b593-a46bc888a7f9";
-GRANT "applicationCreator" TO "5a4dbd41-3fc9-4b3e-b593-a46bc888a7f9";
+GRANT "superadmin" TO "5a4dbd41-3fc9-4b3e-b593-a46bc888a7f9";
 ```
\ No newline at end of file
diff --git a/src/main/java/fr/inra/oresing/model/OreSiRoleForUser.java b/src/main/java/fr/inra/oresing/model/OreSiRoleForUser.java
new file mode 100644
index 000000000..82052b270
--- /dev/null
+++ b/src/main/java/fr/inra/oresing/model/OreSiRoleForUser.java
@@ -0,0 +1,10 @@
+package fr.inra.oresing.model;
+
+import lombok.Value;
+
+@Value
+public class OreSiRoleForUser {
+    private String userId;
+    private String role;
+    private String applicationPattern;
+}
\ No newline at end of file
diff --git a/src/main/java/fr/inra/oresing/model/OreSiUser.java b/src/main/java/fr/inra/oresing/model/OreSiUser.java
index a62fdc075..4227a92c2 100644
--- a/src/main/java/fr/inra/oresing/model/OreSiUser.java
+++ b/src/main/java/fr/inra/oresing/model/OreSiUser.java
@@ -3,9 +3,13 @@ package fr.inra.oresing.model;
 import lombok.Getter;
 import lombok.Setter;
 
+import java.util.LinkedList;
+import java.util.List;
+
 @Getter
 @Setter
 public class OreSiUser extends OreSiEntity {
     private String login;
     private String password;
-}
+    private List<String> authorizations= new LinkedList<>();
+}
\ No newline at end of file
diff --git a/src/main/java/fr/inra/oresing/persistence/AuthenticationService.java b/src/main/java/fr/inra/oresing/persistence/AuthenticationService.java
index b72908524..b27ae37c4 100644
--- a/src/main/java/fr/inra/oresing/persistence/AuthenticationService.java
+++ b/src/main/java/fr/inra/oresing/persistence/AuthenticationService.java
@@ -2,11 +2,9 @@ package fr.inra.oresing.persistence;
 
 import at.favre.lib.crypto.bcrypt.BCrypt;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
 import fr.inra.oresing.model.OreSiUser;
-import fr.inra.oresing.persistence.roles.OreSiApplicationCreatorRole;
-import fr.inra.oresing.persistence.roles.OreSiRole;
-import fr.inra.oresing.persistence.roles.OreSiRoleToAccessDatabase;
-import fr.inra.oresing.persistence.roles.OreSiUserRole;
+import fr.inra.oresing.persistence.roles.*;
 import fr.inra.oresing.rest.CreateUserResult;
 import fr.inra.oresing.rest.LoginResult;
 import fr.inra.oresing.rest.OreSiApiRequestContext;
@@ -17,6 +15,7 @@ import org.springframework.transaction.annotation.Transactional;
 
 import java.util.UUID;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
 
 @Component
 @Transactional
@@ -109,11 +108,51 @@ public class AuthenticationService {
         db.createRole(userRole);
         return new CreateUserResult(result.getId());
     }
+    public OreSiUser addUserRightSuperadmin(UUID userId) {
+        resetRole();
+        final OreSiUser oreSiUser = getOreSiUser(userId);
+        OreSiUserRole roleToModify = getUserRole(userId);
+        OreSiSuperAdminRole roleToAdd = OreSiRole.superAdmin();
+        db.addUserInRole(roleToModify, new OreSiRoleToBeGranted() {
+            @Override
+            public String getAsSqlRole() {
+                return OreSiSuperAdminRole.SUPER_ADMIN.getAsSqlRole();
+            }
+        });
+        return userRepository.findById(userId);
+    }
 
-    public void addUserRightCreateApplication(UUID userId) {
+    public OreSiUser addUserRightCreateApplication(UUID userId, String applicationPattern) {
+        resetRole();
+        final OreSiUser oreSiUser = getOreSiUser(userId);
         OreSiUserRole roleToModify = getUserRole(userId);
+        oreSiUser.getAuthorizations().add(applicationPattern);
         OreSiApplicationCreatorRole roleToAdd = OreSiRole.applicationCreator();
         db.addUserInRole(roleToModify, roleToAdd);
+        final String expression = oreSiUser.getAuthorizations().stream()
+                .map(s -> String.format("%s", s))
+                .collect(Collectors.joining("|", "name ~ '(", ")'"));
+        final SqlPolicy sqlPolicy = new SqlPolicy(
+                String.join("_", OreSiRole.applicationCreator().getAsSqlRole(), userId.toString()),
+                SqlSchema.main().application(),
+                SqlPolicy.PermissiveOrRestrictive.RESTRICTIVE,
+                SqlPolicy.Statement.ALL,
+                new OreSiRole() {
+                    @Override
+                    public String getAsSqlRole() {
+                        return userId.toString();
+                    }
+                },
+                expression
+        );
+        db.createPolicy(sqlPolicy);
+        setRoleForClient();
+        if(!Strings.isNullOrEmpty(applicationPattern)){
+            userRepository.updateAuthorizations(userId, oreSiUser.getAuthorizations());
+            userRepository.flush();
+        }
+        resetRole();
+        return userRepository.findById(userId);
     }
 
     public void removeUser(UUID userId) {
@@ -135,7 +174,21 @@ public class AuthenticationService {
         return getUserRole(user);
     }
 
+    public boolean hasRole(OreSiRole role) {
+        setRoleForClient();
+        final CurrentUserRoles currentUserRoles = userRepository.getRolesForCurrentUser();
+        setRoleAdmin();
+        return currentUserRoles.isSuper() ||
+                currentUserRoles.getCurrentUser().equals(role.getAsSqlRole()) ||
+                currentUserRoles.getMemberOf().contains(role.getAsSqlRole());
+    }
+
+    public boolean isSuperAdmin() {
+        return hasRole(OreSiRole.superAdmin());
+    }
+
     public OreSiUserRole getUserRole(OreSiUser user) {
         return OreSiUserRole.forUser(user);
     }
+
 }
\ No newline at end of file
diff --git a/src/main/java/fr/inra/oresing/persistence/JsonTableRepositoryTemplate.java b/src/main/java/fr/inra/oresing/persistence/JsonTableRepositoryTemplate.java
index c48c72975..4c3572c7f 100644
--- a/src/main/java/fr/inra/oresing/persistence/JsonTableRepositoryTemplate.java
+++ b/src/main/java/fr/inra/oresing/persistence/JsonTableRepositoryTemplate.java
@@ -43,15 +43,18 @@ abstract class JsonTableRepositoryTemplate<T extends OreSiEntity> implements Ini
         // 5min48 pour 100
         // 5min50 pour 500
         // 6min21 pour 1000
+        final String s = namedParameterJdbcTemplate.queryForObject("select CURRENT_USER::TEXT;", Map.of(), String.class);
         return Iterators.partition(stream.iterator(), 50);
     }
 
     public List<UUID> storeAll(Stream<T> stream) {
+        final String s = namedParameterJdbcTemplate.queryForObject("select CURRENT_USER::TEXT;", Map.of(), String.class);
         String query = getUpsertQuery();
         List<UUID> uuids = new LinkedList<>();
         partition(stream).forEachRemaining(entities -> {
             entities.forEach(e -> {
                 if (e.getId() == null) {
+                    final String a = namedParameterJdbcTemplate.queryForObject("select login from oresiuser where id::TEXT=CURRENT_USER::TEXT;", Map.of(), String.class);
                     e.setId(UUID.randomUUID());
                 }
             });
diff --git a/src/main/java/fr/inra/oresing/persistence/SqlService.java b/src/main/java/fr/inra/oresing/persistence/SqlService.java
index 6bddf6220..c4b02d756 100644
--- a/src/main/java/fr/inra/oresing/persistence/SqlService.java
+++ b/src/main/java/fr/inra/oresing/persistence/SqlService.java
@@ -64,7 +64,7 @@ public class SqlService {
 
     public void createPolicy(SqlPolicy sqlPolicy) {
         String createPolicySql = String.format(
-                "CREATE POLICY %s ON %s AS %s FOR %s TO %s USING (%s)",
+                "DROP POLICY IF EXISTS %1$s ON %2$s;CREATE POLICY %1$s ON %2$s AS %3$s FOR %4$s TO %5$s USING (%6$s);",
                 sqlPolicy.getSqlIdentifier(),
                 sqlPolicy.getTable().getSqlIdentifier(),
                 sqlPolicy.getPermissiveOrRestrictive().name(),
@@ -77,7 +77,7 @@ public class SqlService {
 
     public void dropPolicy(SqlPolicy sqlPolicy) {
         String createPolicySql = String.format(
-                "DROP POLICY %s ON %s",
+                "DROP POLICY IF EXISTS %s ON %s",
                 sqlPolicy.getSqlIdentifier(),
                 sqlPolicy.getTable().getSqlIdentifier()
         );
diff --git a/src/main/java/fr/inra/oresing/persistence/UserRepository.java b/src/main/java/fr/inra/oresing/persistence/UserRepository.java
index dfdf47c77..aaae393b8 100644
--- a/src/main/java/fr/inra/oresing/persistence/UserRepository.java
+++ b/src/main/java/fr/inra/oresing/persistence/UserRepository.java
@@ -2,18 +2,23 @@ package fr.inra.oresing.persistence;
 
 import com.google.common.collect.MoreCollectors;
 import fr.inra.oresing.model.OreSiUser;
+import fr.inra.oresing.persistence.roles.CurrentUserRoles;
+import org.springframework.jdbc.core.RowMapper;
 import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
 import org.springframework.stereotype.Component;
 
-import java.util.Optional;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.*;
+import java.util.stream.Collectors;
 
 @Component
 public class UserRepository extends JsonTableRepositoryTemplate<OreSiUser> {
 
     @Override
     protected String getUpsertQuery() {
-        return "INSERT INTO " + getTable().getSqlIdentifier() + " (id, login, password) SELECT id, login, password FROM json_populate_recordset(NULL::" + getTable().getSqlIdentifier() + ", :json::json)"
-                + " ON CONFLICT (id) DO UPDATE SET updateDate=current_timestamp, login=EXCLUDED.login, password=EXCLUDED.password"
+        return "INSERT INTO " + getTable().getSqlIdentifier() + " (id, login, password, authorizations) SELECT id, login, password, authorizations FROM json_populate_recordset(NULL::" + getTable().getSqlIdentifier() + ", :json::json)"
+                + " ON CONFLICT (id) DO UPDATE SET updateDate=current_timestamp, login=EXCLUDED.login, password=EXCLUDED.password, authorizations=EXCLUDED.authorizations"
                 + " RETURNING id";
     }
     
@@ -33,4 +38,54 @@ public class UserRepository extends JsonTableRepositoryTemplate<OreSiUser> {
                 new MapSqlParameterSource("login", login), getJsonRowMapper()).stream().collect(MoreCollectors.toOptional());
         return result;
     }
-}
+
+    public CurrentUserRoles getRolesForRole(String role) {
+        String roleParam = role==null?"\"current_user\"()":String.format("\"%s\"", role);
+        final RowMapper<CurrentUserRoles> rowMapper = new RowMapper<>() {
+
+            @Override
+            public CurrentUserRoles mapRow(ResultSet rs, int rowNum) throws SQLException {
+                String currentUser = rs.getString("currentUser");
+                List<String> memberOf = Arrays.stream((String[])rs.getArray("memberOf").getArray())
+                        .collect(Collectors.toList());
+                Boolean isSuper = rs.getBoolean("isSuper");
+                return new CurrentUserRoles(currentUser, memberOf, isSuper);
+            }
+        };
+        String query = "WITH RECURSIVE membership_tree(grpid, userid, isSuper) AS (\n" +
+                "    SELECT r.oid, r.oid, r.rolsuper isSuper\n" +
+                "    FROM pg_roles r\n" +
+                "    UNION ALL\n" +
+                "    SELECT m_1.roleid, t_1.userid, t_1.isSuper\n" +
+                "    FROM pg_auth_members m_1, membership_tree t_1\n" +
+                "    WHERE m_1.member = t_1.grpid\n" +
+                ")\n" +
+                "SELECT COALESCE(:roleName, CURRENT_USER) \"currentUser\",r.rolname AS usrname,t.isSuper \"isSuper\",\n" +
+                "       array_agg(m.rolname) memberOf\n" +
+                "FROM membership_tree t, pg_roles r, pg_roles m\n" +
+                "WHERE t.grpid = m.oid AND t.userid = r.oid\n" +
+                "and COALESCE(:roleName, CURRENT_USER)=r.rolname\n" +
+                "group by userid, r.rolname,t.isSuper;";
+        final Map<String, String> parameters =new HashMap<>();
+        parameters.put("roleName", role);
+        return  getNamedParameterJdbcTemplate().queryForObject(
+                query,
+                parameters, rowMapper);
+
+
+    }
+    public CurrentUserRoles getRolesForCurrentUser(){
+        return getRolesForRole(null);
+    }
+
+    public int updateAuthorizations(UUID userId, List<String> authorizations) {
+        String query = "update "+getTable().getSqlIdentifier()+" o\n" +
+                "set authorizations = :authorizations\n" +
+                "where id = :uuid::uuid\n";
+        return getNamedParameterJdbcTemplate().update(
+                query,
+                new MapSqlParameterSource("authorizations", authorizations.toArray(String[]::new))
+                        .addValue("uuid",userId )
+        );
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/inra/oresing/persistence/roles/CurrentUserRoles.java b/src/main/java/fr/inra/oresing/persistence/roles/CurrentUserRoles.java
new file mode 100644
index 000000000..46ceefdf3
--- /dev/null
+++ b/src/main/java/fr/inra/oresing/persistence/roles/CurrentUserRoles.java
@@ -0,0 +1,19 @@
+package fr.inra.oresing.persistence.roles;
+
+import lombok.Getter;
+
+import java.util.List;
+
+@Getter
+public class CurrentUserRoles {
+    String currentUser;
+    List<String> memberOf;
+
+    boolean isSuper;
+
+    public CurrentUserRoles(String currentUser, List<String> memberOf, boolean isSuper) {
+        this.currentUser = currentUser;
+        this.memberOf = memberOf;
+        this.isSuper = isSuper;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/inra/oresing/persistence/roles/OreSiRole.java b/src/main/java/fr/inra/oresing/persistence/roles/OreSiRole.java
index c955582fa..20fce6790 100644
--- a/src/main/java/fr/inra/oresing/persistence/roles/OreSiRole.java
+++ b/src/main/java/fr/inra/oresing/persistence/roles/OreSiRole.java
@@ -2,7 +2,9 @@ package fr.inra.oresing.persistence.roles;
 
 import fr.inra.oresing.persistence.WithSqlIdentifier;
 
-public interface OreSiRole extends WithSqlIdentifier {
+@FunctionalInterface
+public interface
+OreSiRole extends WithSqlIdentifier {
 
     static OreSiAnonymousRole anonymous() {
         return OreSiAnonymousRole.ANONYMOUS;
@@ -22,4 +24,4 @@ public interface OreSiRole extends WithSqlIdentifier {
     default String getSqlIdentifier() {
         return "\"" + getAsSqlRole() + "\"";
     }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/fr/inra/oresing/rest/AuthorizationResources.java b/src/main/java/fr/inra/oresing/rest/AuthorizationResources.java
index 374ea71e5..e6e39f079 100644
--- a/src/main/java/fr/inra/oresing/rest/AuthorizationResources.java
+++ b/src/main/java/fr/inra/oresing/rest/AuthorizationResources.java
@@ -2,6 +2,8 @@ package fr.inra.oresing.rest;
 
 import com.google.common.collect.ImmutableSet;
 import fr.inra.oresing.model.OreSiAuthorization;
+import fr.inra.oresing.model.OreSiRoleForUser;
+import fr.inra.oresing.model.OreSiUser;
 import fr.inra.oresing.persistence.roles.OreSiRightOnApplicationRole;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.MediaType;
@@ -38,12 +40,35 @@ public class AuthorizationResources {
         return ResponseEntity.created(URI.create(uri)).body(Map.of("authorizationId", authId.toString()));
     }
 
-    @GetMapping(value = "/applications/{nameOrId}/dataType/{dataType}/authorization/{authorizationId}", produces = MediaType.APPLICATION_JSON_VALUE)
+    @GetMapping(value = "/applications/{nameOrId}/dataType/{dataType}/authorization/{authorizationId}"
+            ,consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
     public ResponseEntity<GetAuthorizationResult> getAuthorization(@PathVariable("nameOrId") String applicationNameOrId, @PathVariable("authorizationId") UUID authorizationId) {
         GetAuthorizationResult getAuthorizationResult = authorizationService.getAuthorization(new AuthorizationRequest(applicationNameOrId, authorizationId));
         return ResponseEntity.ok(getAuthorizationResult);
     }
 
+    @DeleteMapping(value = "/authorization/{role}", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<OreSiUser> deleteAuthorization(
+            @PathVariable(name = "role", required = true)String role,
+            @RequestParam(name = "userId", required = true)String userId,
+            @RequestParam(name = "applicationPattern", required = false)String applicationPattern)
+            throws NotSuperAdminException, NotApplicationCreatorRightsException {
+        final OreSiRoleForUser roleForUser = new OreSiRoleForUser(userId, role, applicationPattern);
+        OreSiUser user = authorizationService.deleteRoleUser(roleForUser);
+        return ResponseEntity.ok(user);
+    }
+
+    @PutMapping(value = "/authorization/{role}", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<OreSiUser> addAuthorization(
+            @PathVariable(name = "role", required = true)String role,
+            @RequestParam(name = "userId", required = true)String userId,
+            @RequestParam(name = "applicationPattern", required = false)String applicationPattern)
+            throws NotSuperAdminException, NotApplicationCreatorRightsException {
+        final OreSiRoleForUser roleForUser = new OreSiRoleForUser(userId, role, applicationPattern);
+        OreSiUser user = authorizationService.addRoleUser(roleForUser);
+        return ResponseEntity.ok(user);
+    }
+
     @GetMapping(value = "/applications/{nameOrId}/dataType/{dataType}/authorization", produces = MediaType.APPLICATION_JSON_VALUE)
     public ResponseEntity<ImmutableSet<GetAuthorizationResult>> getAuthorizations(@PathVariable("nameOrId") String applicationNameOrId, @PathVariable("dataType") String dataType) {
         ImmutableSet<GetAuthorizationResult> getAuthorizationResults = authorizationService.getAuthorizations(applicationNameOrId, dataType);
diff --git a/src/main/java/fr/inra/oresing/rest/AuthorizationService.java b/src/main/java/fr/inra/oresing/rest/AuthorizationService.java
index 9e3ebe6e2..fd79c9dab 100644
--- a/src/main/java/fr/inra/oresing/rest/AuthorizationService.java
+++ b/src/main/java/fr/inra/oresing/rest/AuthorizationService.java
@@ -6,6 +6,7 @@ import fr.inra.oresing.checker.ReferenceLineChecker;
 import fr.inra.oresing.model.*;
 import fr.inra.oresing.persistence.*;
 import fr.inra.oresing.persistence.roles.OreSiRightOnApplicationRole;
+import fr.inra.oresing.persistence.roles.OreSiRole;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
@@ -91,7 +92,7 @@ public class AuthorizationService {
                     authByType.forEach(authorization -> {
                         authorization.getDataGroup()
                                 .forEach(datagroup -> Preconditions.checkArgument(authorizationDescription.getDataGroups().containsKey(datagroup)));
-                        Set<String> labels =Optional.ofNullable( authorizationDescription)
+                        Set<String> labels = Optional.ofNullable(authorizationDescription)
                                 .map(Configuration.AuthorizationDescription::getAuthorizationScopes)
                                 .map(Map::keySet)
                                 .orElseGet(Set::of);
@@ -260,14 +261,14 @@ public class AuthorizationService {
 
     private ImmutableSortedSet<GetGrantableResult.DataGroup> getDataGroups(Application application, String dataType) {
         ImmutableSortedSet<GetGrantableResult.DataGroup> dataGroups =
-        Optional.of(application.getConfiguration().getDataTypes().get(dataType))
-                .map(Configuration.DataTypeDescription::getAuthorization)
-                .map(Configuration.AuthorizationDescription::getDataGroups)
-                .map(dg -> dg.entrySet().stream()
-                    .map(dataGroupEntry -> new GetGrantableResult.DataGroup(dataGroupEntry.getKey(), dataGroupEntry.getValue().getLabel()))
-                    .collect(ImmutableSortedSet.toImmutableSortedSet(Comparator.comparing(GetGrantableResult.DataGroup::getId)) )
-                )
-                .orElseGet(ImmutableSortedSet::of);
+                Optional.of(application.getConfiguration().getDataTypes().get(dataType))
+                        .map(Configuration.DataTypeDescription::getAuthorization)
+                        .map(Configuration.AuthorizationDescription::getDataGroups)
+                        .map(dg -> dg.entrySet().stream()
+                                .map(dataGroupEntry -> new GetGrantableResult.DataGroup(dataGroupEntry.getKey(), dataGroupEntry.getValue().getLabel()))
+                                .collect(ImmutableSortedSet.toImmutableSortedSet(Comparator.comparing(GetGrantableResult.DataGroup::getId)))
+                        )
+                        .orElseGet(ImmutableSortedSet::of);
         return dataGroups;
     }
 
@@ -284,22 +285,22 @@ public class AuthorizationService {
         return Optional.of(application.getConfiguration().getDataTypes().get(dataType))
                 .map(Configuration.DataTypeDescription::getAuthorization)
                 .map(Configuration.AuthorizationDescription::getAuthorizationScopes)
-                .map(authorizationScopes-> authorizationScopes.entrySet().stream()
+                .map(authorizationScopes -> authorizationScopes.entrySet().stream()
                         .map(
-                        authorizationScopeEntry -> {
-                    String variable = authorizationScopeEntry.getValue().getVariable();
-                    String component = authorizationScopeEntry.getValue().getComponent();
-                    VariableComponentKey variableComponentKey = new VariableComponentKey(variable, component);
-                    ReferenceLineChecker referenceLineChecker = referenceLineCheckers.get(variableComponentKey);
-                    String lowestLevelReference = referenceLineChecker.getRefType();
-                    HierarchicalReferenceAsTree hierarchicalReferenceAsTree = oreSiService.getHierarchicalReferenceAsTree(application, lowestLevelReference);
-                    ImmutableSortedSet<GetGrantableResult.AuthorizationScope.Option> rootOptions = hierarchicalReferenceAsTree.getRoots().stream()
-                            .map(rootReferenceValue -> toOption(hierarchicalReferenceAsTree, rootReferenceValue))
-                            .collect(ImmutableSortedSet.toImmutableSortedSet(Comparator.comparing(GetGrantableResult.AuthorizationScope.Option::getId)));
-                    String authorizationScopeId = authorizationScopeEntry.getKey();
-                    return new GetGrantableResult.AuthorizationScope(authorizationScopeId, authorizationScopeId, rootOptions);
-                    })
-                .collect(ImmutableSortedSet.toImmutableSortedSet(Comparator.comparing(GetGrantableResult.AuthorizationScope::getId)))
+                                authorizationScopeEntry -> {
+                                    String variable = authorizationScopeEntry.getValue().getVariable();
+                                    String component = authorizationScopeEntry.getValue().getComponent();
+                                    VariableComponentKey variableComponentKey = new VariableComponentKey(variable, component);
+                                    ReferenceLineChecker referenceLineChecker = referenceLineCheckers.get(variableComponentKey);
+                                    String lowestLevelReference = referenceLineChecker.getRefType();
+                                    HierarchicalReferenceAsTree hierarchicalReferenceAsTree = oreSiService.getHierarchicalReferenceAsTree(application, lowestLevelReference);
+                                    ImmutableSortedSet<GetGrantableResult.AuthorizationScope.Option> rootOptions = hierarchicalReferenceAsTree.getRoots().stream()
+                                            .map(rootReferenceValue -> toOption(hierarchicalReferenceAsTree, rootReferenceValue))
+                                            .collect(ImmutableSortedSet.toImmutableSortedSet(Comparator.comparing(GetGrantableResult.AuthorizationScope.Option::getId)));
+                                    String authorizationScopeId = authorizationScopeEntry.getKey();
+                                    return new GetGrantableResult.AuthorizationScope(authorizationScopeId, authorizationScopeId, rootOptions);
+                                })
+                        .collect(ImmutableSortedSet.toImmutableSortedSet(Comparator.comparing(GetGrantableResult.AuthorizationScope::getId)))
                 )
                 .orElseGet(ImmutableSortedSet::of);
     }
@@ -310,4 +311,59 @@ public class AuthorizationService {
                 .collect(ImmutableSortedSet.toImmutableSortedSet(Comparator.comparing(GetGrantableResult.AuthorizationScope.Option::getId)));
         return new GetGrantableResult.AuthorizationScope.Option(referenceValue.getHierarchicalKey().getSql(), referenceValue.getHierarchicalKey().getSql(), options);
     }
+
+    public OreSiUserResult deleteRoleUser(OreSiRoleForUser roleForUser) {
+        if (OreSiRole.superAdmin().equals(roleForUser.getRole())) {
+            return deleteAdminRoleUser(roleForUser);
+        } else if (OreSiRole.applicationCreator().equals(roleForUser.getRole())) {
+            return deleteApplicationCreatorRoleUser(roleForUser);
+        }
+        throw new BadRoleException("cantSetRole", roleForUser.getRole());
+    }
+
+    private OreSiUserResult deleteApplicationCreatorRoleUser(OreSiRoleForUser oreSiUserRoleApplicationCreator) {
+        throw new NoSuchElementException();
+    }
+
+    private OreSiUserResult deleteAdminRoleUser(OreSiRoleForUser oreSiRoleForUserAdmin) {
+        throw new NoSuchElementException();
+    }
+
+    public OreSiUserResult addRoleUser(OreSiRoleForUser roleForUser) {
+        if (OreSiRole.superAdmin().getAsSqlRole().equals(roleForUser.getRole())) {
+            return addAdminRoleUser(roleForUser);
+        } else if (OreSiRole.applicationCreator().getAsSqlRole().equals(roleForUser.getRole())) {
+            return addApplicationCreatorRoleUser(roleForUser);
+        }
+        throw new BadRoleException("cantSetRole", roleForUser.getRole());
+    }
+
+    private OreSiUserResult addApplicationCreatorRoleUser(OreSiRoleForUser oreSiUserRoleApplicationCreator) {
+        boolean canAddApplicationCreatorRole = false;
+        if (authenticationService.hasRole(OreSiRole.superAdmin())) {
+            canAddApplicationCreatorRole = true;
+        } else if (authenticationService.hasRole(OreSiRole.applicationCreator())) {
+            final OreSiUser user = userRepository.findById(UUID.fromString(oreSiUserRoleApplicationCreator.getUserId()));
+            if (user.getAuthorizations().contains(oreSiUserRoleApplicationCreator.getApplicationPattern())) {
+                canAddApplicationCreatorRole = true;
+            } else {
+                throw new NotApplicationCreatorRightsException(oreSiUserRoleApplicationCreator.getApplicationPattern(), user.getAuthorizations());
+            }
+
+        }
+        if (canAddApplicationCreatorRole) {
+            final OreSiUser user = authenticationService.addUserRightCreateApplication(UUID.fromString(oreSiUserRoleApplicationCreator.getUserId()), oreSiUserRoleApplicationCreator.getApplicationPattern());
+            return new OreSiUserResult(user, userRepository.getRolesForRole(oreSiUserRoleApplicationCreator.getUserId()));
+        }
+        throw new NotSuperAdminException();
+    }
+
+    private OreSiUserResult addAdminRoleUser(OreSiRoleForUser oreSiRoleForUserAdmin) {
+        boolean canAddsupeadmin = false;
+        if (authenticationService.hasRole(OreSiRole.superAdmin())) {
+            OreSiUser user = authenticationService.addUserRightSuperadmin(UUID.fromString(oreSiRoleForUserAdmin.getUserId()));
+            return new OreSiUserResult(user, userRepository.getRolesForRole(oreSiRoleForUserAdmin.getUserId()));
+        }
+        throw new NotSuperAdminException();
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/fr/inra/oresing/rest/BadRoleException.java b/src/main/java/fr/inra/oresing/rest/BadRoleException.java
new file mode 100644
index 000000000..8f818b752
--- /dev/null
+++ b/src/main/java/fr/inra/oresing/rest/BadRoleException.java
@@ -0,0 +1,19 @@
+package fr.inra.oresing.rest;
+
+import fr.inra.oresing.OreSiTechnicalException;
+import lombok.Getter;
+
+@Getter
+public class BadRoleException  extends OreSiTechnicalException {
+    String role;
+
+    public BadRoleException(String message, String role, Throwable cause) {
+        super(message, cause);
+        this.role = role;
+    }
+
+    public BadRoleException(String message, String role) {
+        super(message);
+        this.role = role;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/inra/oresing/rest/NotApplicationCreatorRightsException.java b/src/main/java/fr/inra/oresing/rest/NotApplicationCreatorRightsException.java
new file mode 100644
index 000000000..0f679dfab
--- /dev/null
+++ b/src/main/java/fr/inra/oresing/rest/NotApplicationCreatorRightsException.java
@@ -0,0 +1,27 @@
+package fr.inra.oresing.rest;
+
+import fr.inra.oresing.OreSiTechnicalException;
+
+import java.util.List;
+
+public class NotApplicationCreatorRightsException extends OreSiTechnicalException {
+    public final static String NO_RIGHT_FOR_APPLICATION_CREATION = "NO_RIGHT_FOR_APPLICATION_CREATION";
+    String applicationName;
+    List<String> applicationRestrictions;
+    public NotApplicationCreatorRightsException(String applicationName) {
+        super(NO_RIGHT_FOR_APPLICATION_CREATION);
+        this.applicationName = applicationName;
+        this.applicationRestrictions = List.of();
+    }
+    public NotApplicationCreatorRightsException(String applicationName, List<String> applicationRestrictions) {
+        super(NO_RIGHT_FOR_APPLICATION_CREATION);
+        this.applicationName = applicationName;
+        this.applicationRestrictions = applicationRestrictions;
+    }
+
+    public NotApplicationCreatorRightsException(Throwable cause) {
+        super(NO_RIGHT_FOR_APPLICATION_CREATION, cause);
+        this.applicationName = applicationName;
+        this.applicationRestrictions = List.of();
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/inra/oresing/rest/NotSuperAdminException.java b/src/main/java/fr/inra/oresing/rest/NotSuperAdminException.java
new file mode 100644
index 000000000..cbe5d9015
--- /dev/null
+++ b/src/main/java/fr/inra/oresing/rest/NotSuperAdminException.java
@@ -0,0 +1,14 @@
+package fr.inra.oresing.rest;
+
+import fr.inra.oresing.OreSiTechnicalException;
+
+public class NotSuperAdminException extends OreSiTechnicalException {
+    public final static String SUPER_ADMIN_REQUIRED_FOR_OPERATION = "SUPER_ADMIN_REQUIRED_FOR_OPERATION";
+    public NotSuperAdminException() {
+        super(SUPER_ADMIN_REQUIRED_FOR_OPERATION);
+    }
+
+    public NotSuperAdminException(Throwable cause) {
+        super(SUPER_ADMIN_REQUIRED_FOR_OPERATION, cause);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/inra/oresing/rest/OreSiService.java b/src/main/java/fr/inra/oresing/rest/OreSiService.java
index f3ad71c9e..69d90a8b3 100644
--- a/src/main/java/fr/inra/oresing/rest/OreSiService.java
+++ b/src/main/java/fr/inra/oresing/rest/OreSiService.java
@@ -14,8 +14,7 @@ import fr.inra.oresing.groovy.StringGroovyExpression;
 import fr.inra.oresing.model.*;
 import fr.inra.oresing.model.chart.OreSiSynthesis;
 import fr.inra.oresing.persistence.*;
-import fr.inra.oresing.persistence.roles.OreSiRightOnApplicationRole;
-import fr.inra.oresing.persistence.roles.OreSiUserRole;
+import fr.inra.oresing.persistence.roles.*;
 import fr.inra.oresing.rest.validationcheckresults.DateValidationCheckResult;
 import fr.inra.oresing.rest.validationcheckresults.DefaultValidationCheckResult;
 import fr.inra.oresing.rest.validationcheckresults.ReferenceValidationCheckResult;
@@ -31,6 +30,7 @@ import org.flywaydb.core.Flyway;
 import org.flywaydb.core.api.Location;
 import org.flywaydb.core.api.configuration.ClassicConfiguration;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.BadSqlGrammarException;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
@@ -70,6 +70,13 @@ public class OreSiService {
     @Autowired
     private AuthenticationService authenticationService;
 
+
+    @Autowired
+    private AuthorizationService authorizationService;
+
+    @Autowired
+    private UserRepository userRepository;
+
     @Autowired
     private CheckerFactory checkerFactory;
 
@@ -120,6 +127,9 @@ public class OreSiService {
     }
 
     public UUID createApplication(String name, MultipartFile configurationFile, String comment) throws IOException, BadApplicationConfigurationException {
+        final OreSiUser currentUser = userRepository.findById(request.getRequestClient().getId());
+        final boolean canCreateApplication = currentUser.getAuthorizations().stream()
+                .anyMatch(s -> name.matches(s));
         Application app = new Application();
         app.setName(name);
         app.setComment(comment);
@@ -289,7 +299,20 @@ public class OreSiService {
     }
 
     private UUID changeApplicationConfiguration(Application app, MultipartFile configurationFile, Function<Application, Application> initApplication) throws IOException, BadApplicationConfigurationException {
-
+        final String applicationName = app.getName();
+        final OreSiUser currentUser = userRepository.findById(request.getRequestClient().getId());
+        authenticationService.setRoleForClient();
+        final boolean canCreateApplication = authenticationService.hasRole(OreSiRole.applicationCreator()) &&  currentUser.getAuthorizations().stream()
+                .anyMatch(s -> applicationName.matches(s));
+        final boolean isSuperAdmin = authenticationService.isSuperAdmin();
+        if (!(isSuperAdmin  || canCreateApplication)) {
+            throw new NotApplicationCreatorRightsException(applicationName, currentUser.getAuthorizations());
+        }else if (!isSuperAdmin) {
+            currentUser.getAuthorizations().stream()
+                    .filter(s -> isSuperAdmin || applicationName.matches(s))
+                    .findAny()
+                    .orElseThrow(() -> new NotApplicationCreatorRightsException(applicationName));
+        }
         ConfigurationParsingResult configurationParsingResult;
         if (configurationFile.getOriginalFilename().matches(".*\\.zip")) {
             final byte[] bytes = new MultiYaml().parseConfigurationBytes(configurationFile);
@@ -302,11 +325,15 @@ public class OreSiService {
         app.setReferenceType(new ArrayList<>(configuration.getReferences().keySet()));
         app.setDataType(new ArrayList<>(configuration.getDataTypes().keySet()));
         app.setConfiguration(configuration);
-        app = initApplication.apply(app);
-        UUID confId = storeFile(app, configurationFile, app.getComment());
-        app.setConfigFile(confId);
-        UUID appId = repo.application().store(app);
-        return appId;
+        try {
+            app = initApplication.apply(app);
+            UUID appId = repo.application().store(app);
+            UUID confId = storeFile(app, configurationFile, app.getComment());
+            app.setConfigFile(confId);
+            return appId;
+        } catch (BadSqlGrammarException bsge){
+            throw new NotApplicationCreatorRightsException(applicationName, currentUser.getAuthorizations());
+        }
     }
 
     public UUID addReference(Application app, String refType, MultipartFile file) throws IOException {
@@ -569,13 +596,13 @@ public class OreSiService {
         final Configuration.AuthorizationDescription authorization = dataTypeDescription.getAuthorization();
         final boolean haveAuthorizations = authorization != null;
 
-        final DateLineChecker timeScopeDateLineChecker = haveAuthorizations && authorization.getTimeScope()!=null?
+        final DateLineChecker timeScopeDateLineChecker = haveAuthorizations && authorization.getTimeScope() != null ?
                 lineCheckers.stream()
-                    .filter(lineChecker -> lineChecker instanceof DateLineChecker)
-                    .map(lineChecker -> (DateLineChecker) lineChecker)
-                    .filter(dateLineChecker -> dateLineChecker.getTarget().equals(authorization.getTimeScope()))
-                    .collect(MoreCollectors.onlyElement())
-                :null;
+                        .filter(lineChecker -> lineChecker instanceof DateLineChecker)
+                        .map(lineChecker -> (DateLineChecker) lineChecker)
+                        .filter(dateLineChecker -> dateLineChecker.getTarget().equals(authorization.getTimeScope()))
+                        .collect(MoreCollectors.onlyElement())
+                : null;
 
         return rowWithData -> {
             Datum datum = Datum.copyOf(rowWithData.getDatum());
@@ -611,7 +638,7 @@ public class OreSiService {
                 return Stream.empty();
             }
             LocalDateTimeRange timeScope;
-            if (timeScopeDateLineChecker!=null) {
+            if (timeScopeDateLineChecker != null) {
                 String timeScopeValue = datum.get(authorization.getTimeScope());
                 timeScope = LocalDateTimeRange.parse(timeScopeValue, timeScopeDateLineChecker);
             } else {
@@ -619,12 +646,12 @@ public class OreSiService {
             }
 
             Map<String, Ltree> requiredAuthorizations = new LinkedHashMap<>();
-            if(haveAuthorizations) {
+            if (haveAuthorizations) {
                 authorization.getAuthorizationScopes().forEach((authorizationScope, authorizationScopeDescription) -> {
                     VariableComponentKey variableComponentKey = authorizationScopeDescription.getVariableComponentKey();
                     String requiredAuthorization = datum.get(variableComponentKey);
                     Ltree.checkSyntax(requiredAuthorization);
-                requiredAuthorizations.put(authorizationScope, Ltree.fromSql(requiredAuthorization));
+                    requiredAuthorizations.put(authorizationScope, Ltree.fromSql(requiredAuthorization));
                 });
             }
             checkTimescopRangeInDatasetRange(timeScope, errors, binaryFileDataset, rowWithData.getLineNumber());
@@ -636,13 +663,13 @@ public class OreSiService {
                 return Stream.of((Data) null);
             }
             LinkedHashMap<String, Configuration.DataGroupDescription> dataGroups;
-            if(!haveAuthorizations){
-                dataGroups=new LinkedHashMap<>();
+            if (!haveAuthorizations) {
+                dataGroups = new LinkedHashMap<>();
                 final Configuration.DataGroupDescription dataGroupDescription = new Configuration.DataGroupDescription();
                 dataGroupDescription.setData(dataTypeDescription.getData().keySet());
                 dataGroups.put("_default_", dataGroupDescription);
-            }else{
-                dataGroups= authorization.getDataGroups();
+            } else {
+                dataGroups = authorization.getDataGroups();
             }
             Stream<Data> dataStream = dataGroups.entrySet().stream().map(entry -> {
                 String dataGroup = entry.getKey();
diff --git a/src/main/java/fr/inra/oresing/rest/OreSiUserResult.java b/src/main/java/fr/inra/oresing/rest/OreSiUserResult.java
new file mode 100644
index 000000000..4581252c2
--- /dev/null
+++ b/src/main/java/fr/inra/oresing/rest/OreSiUserResult.java
@@ -0,0 +1,21 @@
+package fr.inra.oresing.rest;
+
+import fr.inra.oresing.model.OreSiUser;
+import fr.inra.oresing.persistence.roles.CurrentUserRoles;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class
+OreSiUserResult extends OreSiUser {
+    private CurrentUserRoles roles;
+
+    public OreSiUserResult(OreSiUser user, CurrentUserRoles userRoles) {
+        super();
+        setLogin(user.getLogin());
+        setAuthorizations(user.getAuthorizations());
+        setId(user.getId());
+        setRoles(userRoles);
+    }
+}
\ No newline at end of file
diff --git a/src/main/resources/migration/main/V1__init_schema.sql b/src/main/resources/migration/main/V1__init_schema.sql
index 0fc93b18c..e04e6306c 100644
--- a/src/main/resources/migration/main/V1__init_schema.sql
+++ b/src/main/resources/migration/main/V1__init_schema.sql
@@ -2,7 +2,8 @@ CREATE EXTENSION IF NOT EXISTS "pgcrypto";
 CREATE EXTENSION IF NOT EXISTS "ltree";
 
 CREATE OR REPLACE FUNCTION fk_check(targetTable TEXT, uid UUID)
-RETURNS BOOLEAN AS $$
+    RETURNS BOOLEAN AS
+$$
 DECLARE
     result TEXT;
 BEGIN
@@ -21,10 +22,11 @@ CREATE OR REPLACE FUNCTION public.jsonb_count_items(IN json jsonb)
     VOLATILE
     PARALLEL UNSAFE
     COST 100
-
-AS $BODY$
-with elements as (select json->jsonb_object_keys(json) element)
-select sum(jsonb_array_length(element)) from elements
+AS
+$BODY$
+with elements as (select json -> jsonb_object_keys(json) element)
+select sum(jsonb_array_length(element))
+from elements
 $BODY$;
 
 /*-- check les foreign key pour le colonne references de la table data
@@ -41,23 +43,26 @@ $$ language 'plpgsql';*/
 
 --check if all elements of oreSiUser array are users
 CREATE OR REPLACE FUNCTION checks_users(users uuid[])
-    RETURNS BOOLEAN AS $$
+    RETURNS BOOLEAN AS
+$$
 DECLARE
     checked BOOLEAN;
 BEGIN
     select users <@ array_agg(id)::uuid[] into checked from OreSiUser OSU group by users;
     return checked;
 END;
-$$  LANGUAGE plpgsql;
+$$ LANGUAGE plpgsql;
 
 
 
 CREATE OR REPLACE FUNCTION name_check(application UUID, targetColumn TEXT, val TEXT)
-RETURNS BOOLEAN AS $$
+    RETURNS BOOLEAN AS
+$$
 DECLARE
     result TEXT;
 BEGIN
-    EXECUTE format('select count(id) > 0 from Application where id=$1 AND $2 = ANY (%s);', targetColumn) INTO result USING application, val;
+    EXECUTE format('select count(id) > 0 from Application where id=$1 AND $2 = ANY (%s);',
+                   targetColumn) INTO result USING application, val;
     RETURN result;
 END;
 $$ language 'plpgsql';
@@ -67,24 +72,28 @@ create domain EntityRef as uuid NOT NULL;
 create domain ListEntityRef as uuid[] NOT NULL;
 create domain DateOrNow as timestamp DEFAULT current_timestamp;
 
-create table OreSiUser (
-    id EntityId PRIMARY KEY,
-    creationDate DateOrNow,
-    updateDate DateOrNow,
-    login Text UNIQUE NOT NULL,
-    password text NOT NULL
+create table OreSiUser
+(
+    id             EntityId PRIMARY KEY,
+    creationDate   DateOrNow,
+    updateDate     DateOrNow,
+    login          Text UNIQUE NOT NULL,
+    password       text        NOT NULL,-- can be null
+    authorizations TEXT[]
 );
 
-create table Application (
-    id EntityId PRIMARY KEY,
-    creationDate DateOrNow,
-    updateDate DateOrNow,
-    name Text,
-    comment TEXT NOT NULL,
+create table Application
+(
+    id            EntityId PRIMARY KEY,
+    creator       name default current_user,
+    creationDate  DateOrNow,
+    updateDate    DateOrNow,
+    name          Text,
+    comment       TEXT NOT NULL,
     referenceType TEXT[], -- liste des types de references existantes
-    dataType TEXT[],      -- liste des types de data existants
+    dataType      TEXT[], -- liste des types de data existants
     configuration jsonb,  -- le fichier de configuration sous forme json
-    configFile uuid CHECK(fk_check(name || '.BinaryFile', configFile))-- can be null
+    configFile    uuid CHECK (fk_check(name || '.BinaryFile', configFile))
 );
 
 CREATE INDEX application_referenceType_gin_idx ON application USING gin (referenceType);
@@ -102,41 +111,44 @@ CREATE ROLE "applicationCreator";
 
 GRANT INSERT, UPDATE ON Application TO "applicationCreator";
 
-GRANT SELECT, UPDATE, DELETE, REFERENCES ON Application TO public;
+GRANT SELECT , UPDATE , DELETE ON OreSiUser TO "superadmin", "applicationCreator";
 
-ALTER TABLE Application ENABLE ROW LEVEL SECURITY;
+GRANT SELECT, UPDATE, DELETE, REFERENCES ON Application TO "applicationCreator",superadmin;
 
-CREATE POLICY "applicationCreator_Application_insert" ON Application AS PERMISSIVE
-            FOR INSERT TO "applicationCreator"
-            WITH CHECK ( true );
+ALTER TABLE Application
+    ENABLE ROW LEVEL SECURITY;
+CREATE POLICY "superadmin_Application_insert"
+    ON Application AS PERMISSIVE
+    TO superadmin
+    using (true)
+    with check (true);
 
-CREATE POLICY "applicationCreator_Application_select" ON Application AS PERMISSIVE
-            FOR SELECT TO "applicationCreator"
-            USING ( true );
 
 CREATE AGGREGATE jsonb_object_agg(jsonb) (SFUNC = 'jsonb_concat', STYPE = jsonb, INITCOND = '{}');
 CREATE AGGREGATE aggregate_by_array_concatenation(anyarray) (SFUNC = 'array_cat', STYPE = anyarray, INITCOND = '{}');
 
-create type COMPOSITE_DATE as (
-  datetimestamp           "timestamp",
-  formattedDate           "varchar"
-) ;
+create type COMPOSITE_DATE as
+(
+    datetimestamp "timestamp",
+    formattedDate "varchar"
+);
 CREATE FUNCTION castTextToCompositeDate(Text) RETURNS COMPOSITE_DATE AS
- 'select
-        (substring($1 from 6 for 19)::timestamp,
+'select (substring($1 from 6 for 19)::timestamp,
          substring($1 from 26))::COMPOSITE_DATE;'
     LANGUAGE SQL
     IMMUTABLE
     RETURNS NULL ON NULL INPUT;
 CREATE CAST (TEXT AS COMPOSITE_DATE) WITH FUNCTION castTextToCompositeDate(Text) AS ASSIGNMENT;
 CREATE FUNCTION castCompositeDateToTimestamp(COMPOSITE_DATE) RETURNS TIMESTAMP
-AS 'select ($1).datetimestamp;'
+AS
+'select ($1).datetimestamp;'
     LANGUAGE SQL
     IMMUTABLE
     RETURNS NULL ON NULL INPUT;
 CREATE CAST (COMPOSITE_DATE AS TIMESTAMP) WITH FUNCTION castCompositeDateToTimestamp(COMPOSITE_DATE) AS ASSIGNMENT;
 CREATE FUNCTION castCompositeDateToFormattedDate(COMPOSITE_DATE) RETURNS Text
-AS 'select ($1).formattedDate;'
+AS
+'select ($1).formattedDate;'
     LANGUAGE SQL
     IMMUTABLE
     RETURNS NULL ON NULL INPUT;
diff --git a/src/test/java/fr/inra/oresing/rest/AuthorizationResourcesTest.java b/src/test/java/fr/inra/oresing/rest/AuthorizationResourcesTest.java
index 8f6eb12e0..db4328f8c 100644
--- a/src/test/java/fr/inra/oresing/rest/AuthorizationResourcesTest.java
+++ b/src/test/java/fr/inra/oresing/rest/AuthorizationResourcesTest.java
@@ -5,7 +5,10 @@ import fr.inra.oresing.OreSiNg;
 import fr.inra.oresing.persistence.AuthenticationService;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
+import org.hamcrest.Matchers;
+import org.hamcrest.core.IsEqual;
 import org.junit.Assert;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -14,15 +17,19 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMock
 import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureWebMvc;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.http.MediaType;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
 import org.springframework.test.annotation.DirtiesContext;
 import org.springframework.test.context.TestExecutionListeners;
 import org.springframework.test.context.TestPropertySource;
 import org.springframework.test.context.junit4.SpringRunner;
 import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
 import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.ResultActions;
 import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
+import org.springframework.transaction.annotation.Transactional;
 
 import javax.servlet.http.Cookie;
+import java.util.Map;
 
 import static org.hamcrest.Matchers.*;
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
@@ -45,31 +52,35 @@ public class AuthorizationResourcesTest {
     @Autowired
     private AuthenticationService authenticationService;
 
+
+    @Autowired
+    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
+
     @Autowired
     private Fixtures fixtures;
 
     @Test
     public void testAddAuthorization() throws Exception {
         Cookie authCookie = fixtures.addApplicationAcbb();
-        CreateUserResult createUserResult = authenticationService.createUser("UnReader" , "xxxxxxxx");
+        CreateUserResult createUserResult = authenticationService.createUser("UnReader", "xxxxxxxx");
         String readerUserId = createUserResult.getUserId().toString();
         Cookie authReaderCookie = mockMvc.perform(post("/api/v1/login")
-                        .param("login" , "UnReader")
-                        .param("password" , "xxxxxxxx"))
+                        .param("login", "UnReader")
+                        .param("password", "xxxxxxxx"))
                 .andReturn().getResponse().getCookie(AuthHelper.JWT_COOKIE_NAME);
 
         {
             String response = mockMvc.perform(get("/api/v1/applications")
                     .cookie(authCookie)
             ).andReturn().getResponse().getContentAsString();
-            Assert.assertTrue("Le créateur de l'application doit pouvoir la retrouver dans la liste" , response.contains("acbb"));
+            Assert.assertTrue("Le créateur de l'application doit pouvoir la retrouver dans la liste", response.contains("acbb"));
         }
 
         {
             String response = mockMvc.perform(get("/api/v1/applications")
                     .cookie(authReaderCookie)
             ).andReturn().getResponse().getContentAsString();
-            Assert.assertFalse("On ne devrait pas voir l'application car les droits n'ont pas encore été accordés" , response.contains("acbb"));
+            Assert.assertFalse("On ne devrait pas voir l'application car les droits n'ont pas encore été accordés", response.contains("acbb"));
         }
 
         {
@@ -89,7 +100,7 @@ public class AuthorizationResourcesTest {
 
         {
             String json = "{\n" +
-                    "   \"usersId\":[\""+readerUserId+"\"],\n" +
+                    "   \"usersId\":[\"" + readerUserId + "\"],\n" +
                     "   \"applicationNameOrId\":\"acbb\",\n" +
                     "   \"id\": null,\n" +
                     "   \"name\": \"une authorization sur acbb\",\n" +
@@ -134,7 +145,7 @@ public class AuthorizationResourcesTest {
             String response = mockMvc.perform(get("/api/v1/applications")
                     .cookie(authReaderCookie)
             ).andReturn().getResponse().getContentAsString();
-            Assert.assertTrue("Une fois l'accès donné, on doit pouvoir avec l'application dans la liste" , response.contains("acbb"));
+            Assert.assertTrue("Une fois l'accès donné, on doit pouvoir avec l'application dans la liste", response.contains("acbb"));
         }
 
         {
@@ -156,11 +167,11 @@ public class AuthorizationResourcesTest {
 
         Cookie authCookie = fixtures.addApplicationHauteFrequence();
 
-        CreateUserResult createUserResult = authenticationService.createUser("UnReader" , "xxxxxxxx");
+        CreateUserResult createUserResult = authenticationService.createUser("UnReader", "xxxxxxxx");
         String readerUserId = createUserResult.getUserId().toString();
         Cookie authReaderCookie = mockMvc.perform(post("/api/v1/login")
-                        .param("login" , "UnReader")
-                        .param("password" , "xxxxxxxx"))
+                        .param("login", "UnReader")
+                        .param("password", "xxxxxxxx"))
                 .andReturn().getResponse().getCookie(AuthHelper.JWT_COOKIE_NAME);
 
         String authorizationId;
@@ -168,7 +179,7 @@ public class AuthorizationResourcesTest {
         {
 
             String json = "{\n" +
-                    "   \"usersId\":[\""+readerUserId+"\"],\n" +
+                    "   \"usersId\":[\"" + readerUserId + "\"],\n" +
                     "   \"applicationNameOrId\":\"hautefrequence\",\n" +
                     "   \"id\": null,\n" +
                     "   \"name\": \"une authorization sur haute fréquence\",\n" +
@@ -235,7 +246,7 @@ public class AuthorizationResourcesTest {
                     .andExpect(jsonPath("$.rows[*].values.localization.projet").value(not(hasItemInArray(equalTo("rnt"))), String[].class))
                     .andExpect(jsonPath("$.rows[*].values.date.day").value(hasItemInArray(equalTo("date:2016-06-14T00:00:00:14/06/2016")), String[].class))
                     .andExpect(jsonPath("$.rows[*].values.date.day").value(not(hasItemInArray(equalTo("date:2017-01-30T00:00:00:30/01/2017"))), String[].class))
-                    .andExpect(jsonPath("$.totalRows" , equalTo(7456)))
+                    .andExpect(jsonPath("$.totalRows", equalTo(7456)))
                     .andReturn().getResponse().getContentAsString();
 
 
@@ -257,8 +268,94 @@ public class AuthorizationResourcesTest {
                             .cookie(authReaderCookie)
                             .accept(MediaType.APPLICATION_JSON))
                     .andExpect(status().isOk())
-                    .andExpect(jsonPath("$.totalRows" , equalTo(-1)))
+                    .andExpect(jsonPath("$.totalRows", equalTo(-1)))
                     .andReturn().getResponse().getContentAsString();
         }
     }
+
+    @Test
+    @Ignore
+    public void testAddApplicationMonsoere() throws Exception {
+        fixtures.addMonsoreApplication();
+    }
+
+    @Test
+    public void testAddRightForAddApplication() throws Exception {
+
+        {
+            final String TEST = "test";
+            CreateUserResult dbUserResult = authenticationService.createUser(TEST, TEST);
+            final Cookie dbUserCookies = mockMvc.perform(post("/api/v1/login")
+                            .param("login", TEST)
+                            .param("password", TEST))
+                    .andReturn().getResponse().getCookie(AuthHelper.JWT_COOKIE_NAME);
+            addRoleAdmin(dbUserResult);
+            String applicationCreatorLogin = "applicationCreator";
+            String applicationCreatorPassword = "xxxxxxxx";
+            CreateUserResult applicationCreatorResult = authenticationService.createUser(applicationCreatorLogin, applicationCreatorPassword);
+            final Cookie applicationCreatorCookies = mockMvc.perform(post("/api/v1/login")
+                            .param("login", applicationCreatorLogin)
+                            .param("password", applicationCreatorPassword))
+                    .andReturn().getResponse().getCookie(AuthHelper.JWT_COOKIE_NAME);
+            String lambdaLogin = "lambda";
+            String lambdaPassword = "xxxxxxxx";
+            CreateUserResult lambdaResult = authenticationService.createUser(lambdaLogin, lambdaPassword);
+            final Cookie lambdaCookie = mockMvc.perform(post("/api/v1/login")
+                            .param("login", lambdaLogin)
+                            .param("password", lambdaPassword))
+                    .andReturn().getResponse().getCookie(AuthHelper.JWT_COOKIE_NAME);
+
+            {
+                //l'administrateur peut créer des applications.
+                fixtures.createApplicationMonSore(dbUserCookies, "monsore");
+            }
+            {
+                // on donne les droits pour un pattern acbb
+
+                ResultActions resultActions = mockMvc.perform(put("/api/v1/authorization/applicationCreator")
+                                .param("userId", applicationCreatorResult.getUserId().toString())
+                                .param("applicationPattern", "acbb")
+                                .cookie(dbUserCookies))
+                        .andExpect(status().is2xxSuccessful())
+                        .andExpect(jsonPath("$.roles.currentUser", IsEqual.equalTo(applicationCreatorResult.getUserId().toString())))
+                        .andExpect(jsonPath("$.roles.memberOf", Matchers.hasItem("applicationCreator")))
+                        .andExpect(jsonPath("$.authorizations", Matchers.hasItem("acbb")))
+                        .andExpect(jsonPath("$.id", IsEqual.equalTo(applicationCreatorResult.getUserId().toString())));
+
+                //on peut déposer acbb
+                fixtures.createApplicationMonSore(applicationCreatorCookies, "acbb");
+
+                //on ne peut déposer monsore
+                try {
+                    fixtures.createApplicationMonSore(applicationCreatorCookies, "monsore");
+                    Assert.fail();
+                } catch (NotApplicationCreatorRightsException e) {
+                    Assert.assertEquals(NotApplicationCreatorRightsException.NO_RIGHT_FOR_APPLICATION_CREATION, e.getMessage());
+                    Assert.assertEquals("monsore", e.applicationName);
+                    Assert.assertTrue(e.applicationRestrictions.contains("acbb"));
+                }
+            }
+            {
+                //on donne des droits pour le pattern monsore
+                ResultActions resultActions = mockMvc.perform(put("/api/v1/authorization/applicationCreator")
+                                .param("userId", applicationCreatorResult.getUserId().toString())
+                                .param("applicationPattern", "monsore")
+                                .cookie(dbUserCookies))
+                        .andExpect(status().is2xxSuccessful())
+                        .andExpect(jsonPath("$.roles.currentUser", IsEqual.equalTo(applicationCreatorResult.getUserId().toString())))
+                        .andExpect(jsonPath("$.roles.memberOf", Matchers.hasItem("applicationCreator")))
+                        .andExpect(jsonPath("$.authorizations", Matchers.hasItem("monsore")))
+                        .andExpect(jsonPath("$.id", IsEqual.equalTo(applicationCreatorResult.getUserId().toString())));
+
+                //on peut déposer monsore
+                fixtures.createApplicationMonSore(applicationCreatorCookies, "monsore");
+            }
+        }
+
+    }
+
+    @Transactional
+    void addRoleAdmin(CreateUserResult dbUserResult) {
+        namedParameterJdbcTemplate.update("grant \"superadmin\" to \"" + dbUserResult.getUserId().toString() + "\"", Map.of());
+    }
 }
\ 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 b2dfa7a65..155705f6f 100644
--- a/src/test/java/fr/inra/oresing/rest/Fixtures.java
+++ b/src/test/java/fr/inra/oresing/rest/Fixtures.java
@@ -5,14 +5,18 @@ import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.io.Resources;
 import fr.inra.oresing.OreSiTechnicalException;
+import fr.inra.oresing.model.OreSiUser;
 import fr.inra.oresing.persistence.AuthenticationService;
 import org.apache.commons.io.IOUtils;
+import org.junit.Assert;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.mock.web.MockMultipartFile;
 import org.springframework.stereotype.Component;
 import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.ResultActions;
 import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
 import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
+import org.springframework.web.util.NestedServletException;
 
 import javax.servlet.http.Cookie;
 import java.io.IOException;
@@ -39,7 +43,6 @@ public class Fixtures {
 
     @Autowired
     private AuthenticationService authenticationService;
-
     private Cookie cookie;
 
     public String getMonsoreApplicationName() {
@@ -249,12 +252,12 @@ public class Fixtures {
         return "/data/migration/couleurs.csv";
     }
 
-    private Cookie addApplicationCreatorUser() throws Exception {
+    public Cookie addSuperAdmin(String applicationPattern) throws Exception {
         if (cookie == null) {
             String aPassword = "xxxxxxxx";
-            String aLogin = "poussin";
+            String aLogin = "superAdmin";
             CreateUserResult createUserResult = authenticationService.createUser(aLogin, aPassword);
-            authenticationService.addUserRightCreateApplication(createUserResult.getUserId());
+            authenticationService.addUserRightCreateApplication(createUserResult.getUserId(), applicationPattern);
             cookie = mockMvc.perform(post("/api/v1/login")
                             .param("login", aLogin)
                             .param("password", aPassword))
@@ -263,15 +266,43 @@ public class Fixtures {
         return cookie;
     }
 
-    public Cookie addMonsoreApplication() throws Exception {
-        Cookie authCookie = addApplicationCreatorUser();
+    public Cookie addApplicationCreatorUser(String applicationPattern) throws Exception {
+        if (cookie == null) {
+            String aPassword = "xxxxxxxx";
+            String aLogin = "poussin";
+            CreateUserResult createUserResult = authenticationService.createUser(aLogin, aPassword);
+            final OreSiUser user = authenticationService.addUserRightCreateApplication(createUserResult.getUserId(), applicationPattern);
+            Assert.assertTrue(user.getAuthorizations().contains(applicationPattern));
+            cookie = mockMvc.perform(post("/api/v1/login")
+                            .param("login", aLogin)
+                            .param("password", aPassword))
+                    .andReturn().getResponse().getCookie(AuthHelper.JWT_COOKIE_NAME);
+        }
+        return cookie;
+    }
+    public String createApplicationMonSore(Cookie authCookie, String applicationName) throws Exception{
+        ResultActions resultActions = null;
         try (InputStream configurationFile = getClass().getResourceAsStream(getMonsoreApplicationConfigurationResourceName())) {
             MockMultipartFile configuration = new MockMultipartFile("file", "monsore.yaml", "text/plain", configurationFile);
-            mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/monsore")
-                            .file(configuration)
-                            .cookie(authCookie))
-                    .andExpect(MockMvcResultMatchers.status().isCreated());
+            resultActions = mockMvc.perform(MockMvcRequestBuilders.multipart(String.format("/api/v1/applications/%s", applicationName==null?"monsore" : applicationName))
+                    .file(configuration)
+                    .cookie(authCookie));
+                    return resultActions.andExpect(MockMvcResultMatchers.status().isCreated())
+                            .andReturn()
+                            .getResponse().getContentAsString();
+        }catch (NestedServletException e){
+            if(e.getCause() instanceof NotApplicationCreatorRightsException) {
+                throw (NotApplicationCreatorRightsException) e.getCause();
+            }
+            throw e;
+        }catch (AssertionError e){
+            return resultActions.andReturn().getResolvedException().getMessage();
         }
+    }
+
+    public Cookie addMonsoreApplication() throws Exception {
+        Cookie authCookie = addApplicationCreatorUser("monsore");
+        String result = createApplicationMonSore(cookie, "monsore");
 
         // Ajout de referentiel
         for (Map.Entry<String, String> e : getMonsoreReferentielFiles().entrySet()) {
@@ -296,7 +327,7 @@ public class Fixtures {
     }
 
     public Cookie addMigrationApplication() throws Exception {
-        Cookie authCookie = addApplicationCreatorUser();
+        Cookie authCookie = addApplicationCreatorUser("fakeapp");
         try (InputStream configurationFile = getClass().getResourceAsStream(getMigrationApplicationConfigurationResourceName(1))) {
             MockMultipartFile configuration = new MockMultipartFile("file", "fake-app.yaml", "text/plain", configurationFile);
             mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/fakeapp")
@@ -327,7 +358,7 @@ public class Fixtures {
     }
 
     public Cookie addApplicationAcbb() throws Exception {
-        Cookie authCookie = addApplicationCreatorUser();
+        Cookie authCookie = addApplicationCreatorUser("acbb");
         try (InputStream configurationFile = getClass().getResourceAsStream(getAcbbApplicationConfigurationResourceName())) {
             MockMultipartFile configuration = new MockMultipartFile("file", "acbb.yaml", "text/plain", configurationFile);
             mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/acbb")
@@ -399,7 +430,7 @@ public class Fixtures {
     }
 
     public Cookie addApplicationHauteFrequence() throws Exception {
-        Cookie authCookie = addApplicationCreatorUser();
+        Cookie authCookie = addApplicationCreatorUser("hautefrequence");
         try (InputStream configurationFile = getClass().getResourceAsStream(getHauteFrequenceApplicationConfigurationResourceName())) {
             MockMultipartFile configuration = new MockMultipartFile("file", "hautefrequence.yaml", "text/plain", configurationFile);
             mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/hautefrequence")
@@ -468,7 +499,7 @@ public class Fixtures {
     }
 
     public Cookie addApplicationOLAC() throws Exception {
-        Cookie authCookie = addApplicationCreatorUser();
+        Cookie authCookie = addApplicationCreatorUser("olac");
         try (InputStream configurationFile = getClass().getResourceAsStream(getOlaApplicationConfigurationResourceName())) {
             MockMultipartFile configuration = new MockMultipartFile("file", "olac.yaml", "text/plain", configurationFile);
             mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/olac")
@@ -629,7 +660,7 @@ public class Fixtures {
     }
 
     public Cookie addApplicationFORET() throws Exception {
-        Cookie authCookie = addApplicationCreatorUser();
+        Cookie authCookie = addApplicationCreatorUser("foret");
         try (InputStream configurationFile = getClass().getResourceAsStream(getForetApplicationConfigurationResourceName())) {
             MockMultipartFile configuration = new MockMultipartFile("file", "foret.yaml", "text/plain", configurationFile);
             mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/foret")
@@ -700,7 +731,7 @@ public class Fixtures {
     }
 
     public void addApplicationRecursivity() throws Exception {
-        Cookie authCookie = addApplicationCreatorUser();
+        Cookie authCookie = addApplicationCreatorUser("recursivite");
         try (InputStream in = getClass().getResourceAsStream(getRecursivityApplicationConfigurationResourceName())) {
             MockMultipartFile configuration = new MockMultipartFile("file", "recursivity.yaml", "text/plain", in);
             mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/recursivite")
diff --git a/src/test/java/fr/inra/oresing/rest/OreSiResourcesTest.java b/src/test/java/fr/inra/oresing/rest/OreSiResourcesTest.java
index 579c02d62..290b75efa 100644
--- a/src/test/java/fr/inra/oresing/rest/OreSiResourcesTest.java
+++ b/src/test/java/fr/inra/oresing/rest/OreSiResourcesTest.java
@@ -31,6 +31,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMock
 import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureWebMvc;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.http.MediaType;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
 import org.springframework.mock.web.MockMultipartFile;
 import org.springframework.test.annotation.DirtiesContext;
 import org.springframework.test.context.TestExecutionListeners;
@@ -42,6 +43,7 @@ import org.springframework.test.web.servlet.ResultActions;
 import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
 import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
 import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.util.NestedServletException;
 
 import javax.servlet.http.Cookie;
@@ -54,9 +56,9 @@ import java.util.*;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
-import static org.hamcrest.Matchers.*;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasItemInArray;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
 
 @RunWith(SpringRunner.class)
@@ -88,14 +90,25 @@ public class OreSiResourcesTest {
     private UUID userId;
     private CreateUserResult lambdaUser;
 
+    @Autowired
+    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
+
+
     @Before
     public void createUser() throws Exception {
-        userId = authenticationService.createUser("poussin", "xxxxxxxx").getUserId();
+        final CreateUserResult authUser = authenticationService.createUser("poussin", "xxxxxxxx");
+        userId = authUser.getUserId();
         lambdaUser = authenticationService.createUser("lambda", "xxxxxxxx");
         authCookie = mockMvc.perform(post("/api/v1/login")
                         .param("login", "poussin")
                         .param("password", "xxxxxxxx"))
                 .andReturn().getResponse().getCookie(AuthHelper.JWT_COOKIE_NAME);
+        addRoleAdmin(authUser);
+    }
+
+    @Transactional
+    void addRoleAdmin(CreateUserResult dbUserResult) {
+        namedParameterJdbcTemplate.update("grant \"superadmin\" to \"" + dbUserResult.getUserId().toString() + "\"", Map.of());
     }
 
     @Test
@@ -103,21 +116,35 @@ public class OreSiResourcesTest {
     public void addApplicationMonsore() throws Exception {
         String appId;
 
+        final CreateUserResult monsoereUser = authenticationService.createUser("monsore", "xxxxxxxx");
+        UUID monsoreUserId = monsoereUser.getUserId();
+        Cookie monsoreCookie = mockMvc.perform(post("/api/v1/login")
+                        .param("login", "monsore")
+                        .param("password", "xxxxxxxx"))
+                .andReturn().getResponse().getCookie(AuthHelper.JWT_COOKIE_NAME);
+
         URL resource = getClass().getResource(fixtures.getMonsoreApplicationConfigurationResourceName());
         try (InputStream in = Objects.requireNonNull(resource).openStream()) {
             MockMultipartFile configuration = new MockMultipartFile("file", "monsore.yaml", "text/plain", in);
 
             // on n'a pas le droit de creer de nouvelle application
-            mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/monsore")
-                            .file(configuration)
-                            .cookie(authCookie))
-                    .andExpect(status().is4xxClientError());
-            authenticationService.addUserRightCreateApplication(userId);
+            try {
+                mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/monsore")
+                                .file(configuration)
+                                .cookie(monsoreCookie))
+                        .andExpect(status().is4xxClientError());
+                addUserRightCreateApplication(monsoreUserId, "monsore");
+                Assert.fail();
+            }catch (NestedServletException e){
+                final NotApplicationCreatorRightsException cause = (NotApplicationCreatorRightsException) e.getCause();
+                Assert.assertEquals("monsore", cause.applicationName);
+            }
+            addUserRightCreateApplication(monsoreUserId, "monsore");
 
             String response = mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/monsore")
                             .file(configuration)
                             .param("comment", "commentaire")
-                            .cookie(authCookie))
+                            .cookie(monsoreCookie))
                     .andExpect(status().isCreated())
                     .andExpect(jsonPath("$.id", IsNull.notNullValue()))
                     .andReturn().getResponse().getContentAsString();
@@ -127,7 +154,7 @@ public class OreSiResourcesTest {
 
         String response = mockMvc.perform(get("/api/v1/applications/{appId}", appId)
                         .contentType(MediaType.APPLICATION_JSON)
-                        .cookie(authCookie))
+                        .cookie(monsoreCookie))
                 .andExpect(status().isOk())
                 .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
                 // id
@@ -148,7 +175,7 @@ public class OreSiResourcesTest {
 
                 response = mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/monsore/references/{refType}", e.getKey())
                                 .file(refFile)
-                                .cookie(authCookie))
+                                .cookie(monsoreCookie))
                         .andExpect(status().isCreated())
                         .andExpect(jsonPath("$.id", IsNull.notNullValue()))
                         .andReturn().getResponse().getContentAsString();
@@ -158,13 +185,13 @@ public class OreSiResourcesTest {
         }
         mockMvc.perform(get("/api/v1/applications/{appId}", appId)
                         .contentType(MediaType.APPLICATION_JSON)
-                        .cookie(authCookie))
+                        .cookie(monsoreCookie))
                 .andExpect(status().isOk())
                 .andExpect(jsonPath("$.referenceSynthesis[ ?(@.referenceType=='valeurs_qualitatives')].lineCount", IsEqual.equalTo(List.of(3))));
 
         String getReferencesResponse = mockMvc.perform(get("/api/v1/applications/monsore/references/sites")
                         .contentType(MediaType.APPLICATION_JSON)
-                        .cookie(authCookie))
+                        .cookie(monsoreCookie))
                 .andExpect(status().isOk())
                 .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
                 .andReturn().getResponse().getContentAsString();
@@ -179,7 +206,7 @@ public class OreSiResourcesTest {
 
             response = mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/monsore/data/pem")
                             .file(refFile)
-                            .cookie(authCookie))
+                            .cookie(monsoreCookie))
                     .andExpect(status().is2xxSuccessful())
                     .andReturn().getResponse().getContentAsString();
 
@@ -193,7 +220,7 @@ public class OreSiResourcesTest {
             MockMultipartFile refFile = new MockMultipartFile("file", "data-pem.csv", "text/plain", bytes);
             response = mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/monsore/data/pem")
                             .file(refFile)
-                            .cookie(authCookie))
+                            .cookie(monsoreCookie))
                     .andExpect(status().isBadRequest())
                     .andReturn().getResponse().getContentAsString();
             log.debug(StringUtils.abbreviate(response, 50));
@@ -203,7 +230,7 @@ public class OreSiResourcesTest {
 
         // list des types de data
         response = mockMvc.perform(get("/api/v1/applications/monsore/data")
-                        .cookie(authCookie))
+                        .cookie(monsoreCookie))
                 .andExpect(status().isOk())
                 .andReturn().getResponse().getContentAsString();
 
@@ -232,7 +259,7 @@ public class OreSiResourcesTest {
             }
 
             String actualJson = mockMvc.perform(get("/api/v1/applications/monsore/data/pem")
-                            .cookie(authCookie)
+                            .cookie(monsoreCookie)
                             .accept(MediaType.APPLICATION_JSON))
                     .andExpect(status().isOk())
                     .andExpect(jsonPath("$.variables").isArray())
@@ -266,7 +293,7 @@ public class OreSiResourcesTest {
             String filter = "{\"application\":null,\"applicationNameOrId\":null,\"dataType\":null,\"offset\":null,\"limit\":15,\"variableComponentSelects\":[],\"variableComponentFilters\":[{\"variableComponentKey\":{\"variable\":\"date\",\"component\":\"value\"},\"filter\":null,\"type\":\"date\",\"format\":\"dd/MM/yyyy\",\"intervalValues\":{\"from\":\"1984-01-01\",\"to\":\"1984-01-01\"}},{\"variableComponentKey\":{\"variable\":\"Nombre d'individus\",\"component\":\"value\"},\"filter\":null,\"type\":\"numeric\",\"format\":\"integer\",\"intervalValues\":{\"from\":\"20\",\"to\":\"29\"}},{\"variableComponentKey\":{\"variable\":\"Couleur des individus\",\"component\":\"value\"},\"filter\":\"vert\",\"type\":\"reference\",\"format\":\"uuid\",\"intervalValues\":null}],\"variableComponentOrderBy\":[{\"variableComponentKey\":{\"variable\":\"site\",\"component\":\"plateforme\"},\"order\":\"ASC\",\"type\":null,\"format\":null}]}";
             Resources.toString(Objects.requireNonNull(getClass().getResource("/data/monsore/compare/export.json")), Charsets.UTF_8);
             String actualJson = mockMvc.perform(get("/api/v1/applications/monsore/data/pem")
-                            .cookie(authCookie)
+                            .cookie(monsoreCookie)
                             .param("downloadDatasetQuery", filter)
                             .accept(MediaType.APPLICATION_JSON))
                     .andExpect(status().isOk())
@@ -284,7 +311,7 @@ public class OreSiResourcesTest {
         {
             String expectedCsv = Resources.toString(Objects.requireNonNull(getClass().getResource("/data/monsore/compare/export.csv")), Charsets.UTF_8);
             String actualCsv = mockMvc.perform(get("/api/v1/applications/monsore/data/pem")
-                            .cookie(authCookie)
+                            .cookie(monsoreCookie)
                             .accept(MediaType.TEXT_PLAIN))
                     .andExpect(status().isOk())
                     //     .andExpect(content().string(expectedCsv))
@@ -308,7 +335,7 @@ public class OreSiResourcesTest {
             MockMultipartFile refFile = new MockMultipartFile("file", "data-pem.csv", "text/plain", invalidCsv.getBytes(StandardCharsets.UTF_8));
             response = mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/monsore/data/pem")
                             .file(refFile)
-                            .cookie(authCookie))
+                            .cookie(monsoreCookie))
                     .andExpect(status().is4xxClientError())
                     .andReturn().getResponse().getContentAsString();
             log.debug(StringUtils.abbreviate(response, 50));
@@ -408,7 +435,7 @@ public class OreSiResourcesTest {
         try (InputStream in = Objects.requireNonNull(resource).openStream()) {
             MockMultipartFile configuration = new MockMultipartFile("file", "monsore.yaml", "text/plain", in);
             //définition de l'application
-            authenticationService.addUserRightCreateApplication(userId);
+            addUserRightCreateApplication(userId, "monsore");
 
             String response = mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/monsore")
                             .file(configuration)
@@ -602,7 +629,7 @@ public class OreSiResourcesTest {
         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);
+            addUserRightCreateApplication(userId, "progressive");
 
             String result = mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/progressive")
                             .file(configuration)
@@ -701,7 +728,7 @@ public class OreSiResourcesTest {
         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);
+            addUserRightCreateApplication(userId, "progressive");
 
             String result = mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/progressive")
                             .file(configuration)
@@ -727,7 +754,7 @@ public class OreSiResourcesTest {
         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);
+            addUserRightCreateApplication(userId, "progressive");
 
             BadApplicationConfigurationException exception = (BadApplicationConfigurationException) mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/progressive")
                             .file(configuration)
@@ -753,7 +780,7 @@ public class OreSiResourcesTest {
         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);
+            addUserRightCreateApplication(userId, "progressive");
 
             final ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/progressive")
                             .file(configuration)
@@ -772,7 +799,7 @@ public class OreSiResourcesTest {
         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);
+            addUserRightCreateApplication(userId, "progressive");
 
             final ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/progressive")
                             .file(configuration)
@@ -796,7 +823,7 @@ public class OreSiResourcesTest {
         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);
+            addUserRightCreateApplication(userId, "progressive");
 
             String response = mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/progressive")
                             .file(configuration)
@@ -851,7 +878,7 @@ public class OreSiResourcesTest {
         try (InputStream in = Objects.requireNonNull(resource).openStream()) {
             MockMultipartFile configuration = new MockMultipartFile("file", "recursivity.yaml", "text/plain", in);
             //définition de l'application
-            authenticationService.addUserRightCreateApplication(userId);
+            addUserRightCreateApplication(userId, "recursivite");
 
             String response = mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/recursivite")
                             .file(configuration)
@@ -867,9 +894,9 @@ public class OreSiResourcesTest {
                             .cookie(authCookie))
                     .andExpect(status().is2xxSuccessful())
                     .andExpect(jsonPath("$.references.taxon.dynamicColumns['propriétés de taxons'].reference", IsEqual.equalTo("proprietes_taxon")))
-                   .andExpect(jsonPath("$.references.taxon.dynamicColumns['propriétés de taxons'].headerPrefix", IsEqual.equalTo("pt_")))
-                   .andExpect(jsonPath("$.internationalization.references.taxon.internationalizedDynamicColumns['propriétés de taxons'].en", IsEqual.equalTo("Properties of Taxa")))
-                     .andReturn().getResponse().getContentAsString();
+                    .andExpect(jsonPath("$.references.taxon.dynamicColumns['propriétés de taxons'].headerPrefix", IsEqual.equalTo("pt_")))
+                    .andExpect(jsonPath("$.internationalization.references.taxon.internationalizedDynamicColumns['propriétés de taxons'].en", IsEqual.equalTo("Properties of Taxa")))
+                    .andReturn().getResponse().getContentAsString();
 
         }
 
@@ -928,8 +955,7 @@ public class OreSiResourcesTest {
     @Test
     @Category(ACBB_TEST.class)
     public void addApplicationAcbb() throws Exception {
-        authenticationService.addUserRightCreateApplication(userId);
-
+        addUserRightCreateApplication(userId, "acbb");
         URL resource = getClass().getResource(fixtures.getAcbbApplicationConfigurationResourceName());
         assert resource != null;
         try (InputStream in = resource.openStream()) {
@@ -951,6 +977,18 @@ public class OreSiResourcesTest {
         addDataSWC();
     }
 
+    private void addUserRightCreateApplication(UUID userId, String pattern) throws Exception {
+        ResultActions resultActions = mockMvc.perform(put("/api/v1/authorization/applicationCreator")
+                        .param("userId", userId.toString())
+                        .param("applicationPattern", pattern)
+                        .cookie(authCookie))
+                .andExpect(status().is2xxSuccessful())
+                .andExpect(jsonPath("$.roles.currentUser", IsEqual.equalTo(userId.toString())))
+                .andExpect(jsonPath("$.roles.memberOf", Matchers.hasItem("applicationCreator")))
+                .andExpect(jsonPath("$.authorizations", Matchers.hasItem(pattern)))
+                .andExpect(jsonPath("$.id", IsEqual.equalTo(userId.toString())));
+    }
+
     private void addDataSWC() throws Exception {
         try (InputStream in = fixtures.openSwcDataResourceName(true)) {
             MockMultipartFile file = new MockMultipartFile("file", "SWC.csv", "text/plain", in);
@@ -1106,7 +1144,7 @@ public class OreSiResourcesTest {
     @Test
     @Category(HAUTE_FREQUENCE_TEST.class)
     public void addApplicationHauteFrequence() throws Exception {
-        authenticationService.addUserRightCreateApplication(userId);
+        addUserRightCreateApplication(userId, "hautefrequence");
         try (InputStream configurationFile = fixtures.getClass().getResourceAsStream(fixtures.getHauteFrequenceApplicationConfigurationResourceName())) {
             MockMultipartFile configuration = new MockMultipartFile("file", "hautefrequence.yaml", "text/plain", configurationFile);
             mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/hautefrequence")
@@ -1139,7 +1177,7 @@ public class OreSiResourcesTest {
     @Test
     @Category(OTHERS_TEST.class)
     public void addDuplicatedTest() throws Exception {
-        authenticationService.addUserRightCreateApplication(userId);
+        addUserRightCreateApplication(userId, "duplicated");
         try (InputStream configurationFile = fixtures.getClass().getResourceAsStream(fixtures.getDuplicatedApplicationConfigurationResourceName())) {
             MockMultipartFile configuration = new MockMultipartFile("file", "duplicated.yaml", "text/plain", configurationFile);
             mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/duplicated")
@@ -1394,7 +1432,7 @@ on test le dépôt d'un fichier récursif
     @Test
     @Category(OTHERS_TEST.class)
     public void addApplicationOLAC() throws Exception {
-        authenticationService.addUserRightCreateApplication(userId);
+        addUserRightCreateApplication(userId, "olac");
         try (InputStream configurationFile = fixtures.getClass().getResourceAsStream(fixtures.getOlaApplicationConfigurationResourceName())) {
             MockMultipartFile configuration = new MockMultipartFile("file", "olac.yaml", "text/plain", configurationFile);
             mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/olac")
@@ -1482,7 +1520,7 @@ on test le dépôt d'un fichier récursif
     @Test
     @Category(OTHERS_TEST.class)
     public void addApplicationFORET_essai() throws Exception {
-        authenticationService.addUserRightCreateApplication(userId);
+        addUserRightCreateApplication(userId, "foret");
         try (InputStream configurationFile = fixtures.getClass().getResourceAsStream(fixtures.getForetEssaiApplicationConfigurationResourceName())) {
             MockMultipartFile configuration = new MockMultipartFile("file", "foret_essai.yaml", "text/plain", configurationFile);
             mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/foret")
@@ -1514,7 +1552,7 @@ on test le dépôt d'un fichier récursif
 
                 if (entry.getKey().equals("swc_j")) {
                     authenticationService.setRoleAdmin();
-                    final String responseForBuildSynthesis = mockMvc.perform(MockMvcRequestBuilders.put("/api/v1/applications/foret/synthesis/{refType}", entry.getKey())
+                    final String responseForBuildSynthesis = mockMvc.perform(put("/api/v1/applications/foret/synthesis/{refType}", entry.getKey())
                                     .cookie(authCookie))
                             .andExpect(jsonPath("$.SWC", Matchers.hasSize(8)))
                             .andReturn().getResponse().getContentAsString();
@@ -1531,7 +1569,7 @@ on test le dépôt d'un fichier récursif
     @Test
     @Category(OTHERS_TEST.class)
     public void addApplicationFORET() throws Exception {
-        authenticationService.addUserRightCreateApplication(userId);
+        addUserRightCreateApplication(userId, "foret");
         try (InputStream configurationFile = fixtures.getClass().getResourceAsStream(fixtures.getForetApplicationConfigurationResourceName())) {
             MockMultipartFile configuration = new MockMultipartFile("file", "foret.yaml", "text/plain", configurationFile);
             mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/foret")
-- 
GitLab


From e1ce6276d32c97b058cafcd4ea6eb9134be411d0 Mon Sep 17 00:00:00 2001
From: TCHERNIATINSKY <philippe.tcherniatinsky@inrae.fr>
Date: Fri, 6 May 2022 16:26:06 +0200
Subject: [PATCH 2/3] Ajout de droits de haut niveau #41
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- superadmin peut ajouter/suprimer le rôle superadmin a un utilisateur

- superadmin peut ajouter/suprimer le rôle applicationCreator a un utilisateur
---
 .../persistence/AuthenticationService.java    | 53 ++++++++++++-
 .../oresing/rest/AuthorizationRequest.java    |  4 +-
 .../oresing/rest/AuthorizationResources.java  | 17 ++--
 .../oresing/rest/AuthorizationService.java    | 69 ++++++++++++----
 .../oresing/rest/OreExceptionHandler.java     | 11 +++
 .../fr/inra/oresing/rest/OreSiService.java    | 10 +--
 .../migration/main/V1__init_schema.sql        |  2 +
 .../rest/AuthorizationResourcesTest.java      | 26 ++++--
 .../java/fr/inra/oresing/rest/Fixtures.java   | 79 ++++++++++++++-----
 .../inra/oresing/rest/OreSiResourcesTest.java | 51 +++++++++---
 10 files changed, 252 insertions(+), 70 deletions(-)

diff --git a/src/main/java/fr/inra/oresing/persistence/AuthenticationService.java b/src/main/java/fr/inra/oresing/persistence/AuthenticationService.java
index b27ae37c4..8b2bbf38a 100644
--- a/src/main/java/fr/inra/oresing/persistence/AuthenticationService.java
+++ b/src/main/java/fr/inra/oresing/persistence/AuthenticationService.java
@@ -108,6 +108,20 @@ public class AuthenticationService {
         db.createRole(userRole);
         return new CreateUserResult(result.getId());
     }
+
+    public OreSiUser deleteUserRightSuperadmin(UUID userId) {
+        resetRole();
+        final OreSiUser oreSiUser = getOreSiUser(userId);
+        OreSiUserRole roleToModify = getUserRole(userId);
+        OreSiSuperAdminRole roleToRevoke = OreSiRole.superAdmin();
+        db.removeUserInRole(roleToModify, new OreSiRoleToBeGranted() {
+            @Override
+            public String getAsSqlRole() {
+                return OreSiSuperAdminRole.SUPER_ADMIN.getAsSqlRole();
+            }
+        });
+        return userRepository.findById(userId);
+    }
     public OreSiUser addUserRightSuperadmin(UUID userId) {
         resetRole();
         final OreSiUser oreSiUser = getOreSiUser(userId);
@@ -122,6 +136,44 @@ public class AuthenticationService {
         return userRepository.findById(userId);
     }
 
+    public OreSiUser deleteUserRightCreateApplication(UUID userId, String applicationPattern) {
+        resetRole();
+        final OreSiUser oreSiUser = getOreSiUser(userId);
+        OreSiUserRole roleToModify = getUserRole(userId);
+        oreSiUser.getAuthorizations().remove(applicationPattern);
+        OreSiApplicationCreatorRole roleToAdd = OreSiRole.applicationCreator();
+        db.removeUserInRole(roleToModify, roleToAdd);
+        final String expression = oreSiUser.getAuthorizations().stream()
+                .map(s -> String.format("%s", s))
+                .collect(Collectors.joining("|", "name ~ '(", ")'"));
+        final SqlPolicy sqlPolicy = new SqlPolicy(
+                String.join("_", OreSiRole.applicationCreator().getAsSqlRole(), userId.toString()),
+                SqlSchema.main().application(),
+                SqlPolicy.PermissiveOrRestrictive.RESTRICTIVE,
+                SqlPolicy.Statement.ALL,
+                new OreSiRole() {
+                    @Override
+                    public String getAsSqlRole() {
+                        return userId.toString();
+                    }
+                },
+                expression
+        );
+        if(oreSiUser.getAuthorizations().isEmpty()){
+            db.dropPolicy(sqlPolicy);
+        }else{
+            db.createPolicy(sqlPolicy);
+        }
+
+        setRoleForClient();
+        if(!Strings.isNullOrEmpty(applicationPattern)){
+            userRepository.updateAuthorizations(userId, oreSiUser.getAuthorizations());
+            userRepository.flush();
+        }
+        resetRole();
+        return userRepository.findById(userId);
+    }
+
     public OreSiUser addUserRightCreateApplication(UUID userId, String applicationPattern) {
         resetRole();
         final OreSiUser oreSiUser = getOreSiUser(userId);
@@ -190,5 +242,4 @@ public class AuthenticationService {
     public OreSiUserRole getUserRole(OreSiUser user) {
         return OreSiUserRole.forUser(user);
     }
-
 }
\ No newline at end of file
diff --git a/src/main/java/fr/inra/oresing/rest/AuthorizationRequest.java b/src/main/java/fr/inra/oresing/rest/AuthorizationRequest.java
index b33be67ac..3e0a8d20c 100644
--- a/src/main/java/fr/inra/oresing/rest/AuthorizationRequest.java
+++ b/src/main/java/fr/inra/oresing/rest/AuthorizationRequest.java
@@ -9,5 +9,7 @@ public class AuthorizationRequest {
 
     String applicationNameOrId;
 
+    String dataType;
+
     UUID authorizationId;
-}
+}
\ No newline at end of file
diff --git a/src/main/java/fr/inra/oresing/rest/AuthorizationResources.java b/src/main/java/fr/inra/oresing/rest/AuthorizationResources.java
index e6e39f079..9f345d462 100644
--- a/src/main/java/fr/inra/oresing/rest/AuthorizationResources.java
+++ b/src/main/java/fr/inra/oresing/rest/AuthorizationResources.java
@@ -30,20 +30,15 @@ public class AuthorizationResources {
         Set<UUID> previousUsers = authorization.getUuid()==null?new HashSet<>():authorization.getUsersId();
         OreSiAuthorization oreSiAuthorization = authorizationService.addAuthorization(authorization);
         UUID authId = oreSiAuthorization.getId();
-            OreSiRightOnApplicationRole roleForAuthorization = null;
-        if(authorization.getUuid()==null){
-             roleForAuthorization = authorizationService.createRoleForAuthorization(authorization, oreSiAuthorization);
-        }
-
+        OreSiRightOnApplicationRole roleForAuthorization =  authorizationService.createRoleForAuthorization(authorization, oreSiAuthorization);
         authorizationService.updateRoleForManagement(previousUsers, oreSiAuthorization);
         String uri = UriUtils.encodePath("/applications/" + authorization.getApplicationNameOrId() + "/dataType/" + authorization.getDataType() + "/authorization/" + authId.toString(), Charset.defaultCharset());
         return ResponseEntity.created(URI.create(uri)).body(Map.of("authorizationId", authId.toString()));
     }
 
-    @GetMapping(value = "/applications/{nameOrId}/dataType/{dataType}/authorization/{authorizationId}"
-            ,consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
-    public ResponseEntity<GetAuthorizationResult> getAuthorization(@PathVariable("nameOrId") String applicationNameOrId, @PathVariable("authorizationId") UUID authorizationId) {
-        GetAuthorizationResult getAuthorizationResult = authorizationService.getAuthorization(new AuthorizationRequest(applicationNameOrId, authorizationId));
+    @GetMapping(value = "/applications/{nameOrId}/dataType/{dataType}/authorization/{authorizationId}", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<GetAuthorizationResult> getAuthorization(@PathVariable("nameOrId") String applicationNameOrId,@PathVariable("dataType") String dataType, @PathVariable("authorizationId") UUID authorizationId) {
+        GetAuthorizationResult getAuthorizationResult = authorizationService.getAuthorization(new AuthorizationRequest(applicationNameOrId,dataType,  authorizationId));
         return ResponseEntity.ok(getAuthorizationResult);
     }
 
@@ -82,8 +77,8 @@ public class AuthorizationResources {
     }
 
     @DeleteMapping(value = "/applications/{nameOrId}/dataType/{dataType}/authorization/{authorizationId}", produces = MediaType.APPLICATION_JSON_VALUE)
-    public ResponseEntity<?> revokeAuthorization(@PathVariable("nameOrId") String applicationNameOrId, @PathVariable("authorizationId") UUID authorizationId) {
-        authorizationService.revoke(new AuthorizationRequest(applicationNameOrId, authorizationId));
+    public ResponseEntity<?> revokeAuthorization(@PathVariable("nameOrId") String applicationNameOrId, @PathVariable("dataType") String dataType,  @PathVariable("authorizationId") UUID authorizationId) {
+        authorizationService.revoke(new AuthorizationRequest(applicationNameOrId, dataType,  authorizationId));
         return ResponseEntity.noContent().build();
     }
 }
\ No newline at end of file
diff --git a/src/main/java/fr/inra/oresing/rest/AuthorizationService.java b/src/main/java/fr/inra/oresing/rest/AuthorizationService.java
index fd79c9dab..f13f3a7fd 100644
--- a/src/main/java/fr/inra/oresing/rest/AuthorizationService.java
+++ b/src/main/java/fr/inra/oresing/rest/AuthorizationService.java
@@ -48,6 +48,17 @@ public class AuthorizationService {
         OreSiRightOnApplicationRole oreSiRightOnApplicationRole = OreSiRightOnApplicationRole.managementRole(application, modifiedAuthorization.getId());
         db.addUserInRole(oreSiRightOnApplicationRole, OreSiRightOnApplicationRole.readerOn(application));
         addOrRemoveAuthorizationForUsers(previousUsers, newUsers, oreSiRightOnApplicationRole);
+
+        final String expression = String.format("name = '%s'",application.getName());
+        final SqlPolicy sqlPolicy = new SqlPolicy(
+                String.join("_", "application","reader", oreSiRightOnApplicationRole.getAsSqlRole()),
+                SqlSchema.main().application(),
+                SqlPolicy.PermissiveOrRestrictive.PERMISSIVE,
+                SqlPolicy.Statement.SELECT,
+                oreSiRightOnApplicationRole,
+                expression
+        );
+        db.createPolicy(sqlPolicy);
         if (modifiedAuthorization.getAuthorizations().containsKey(OperationType.publication)) {
             db.addUserInRole(oreSiRightOnApplicationRole, OreSiRightOnApplicationRole.writerOn(application));
             SqlPolicy publishPolicy = toDatatypePolicy(modifiedAuthorization, oreSiRightOnApplicationRole, OperationType.publication, SqlPolicy.Statement.INSERT);
@@ -171,6 +182,15 @@ public class AuthorizationService {
         UUID authorizationId = revokeAuthorizationRequest.getAuthorizationId();
         OreSiAuthorization oreSiAuthorization = authorizationRepository.findById(authorizationId);
         OreSiRightOnApplicationRole oreSiRightOnApplicationRole = OreSiRightOnApplicationRole.managementRole(application, authorizationId);
+        final SqlPolicy sqlPolicy = new SqlPolicy(
+                String.join("_", "application","reader", oreSiRightOnApplicationRole.getAsSqlRole()),
+                SqlSchema.main().application(),
+                null,
+                null,
+                null,
+                null
+        );
+        db.dropPolicy(sqlPolicy);
         if (oreSiAuthorization.getAuthorizations().containsKey(OperationType.publication)) {
             db.addUserInRole(oreSiRightOnApplicationRole, OreSiRightOnApplicationRole.writerOn(application));
             SqlPolicy publishPolicy = toDatatypePolicy(oreSiAuthorization, oreSiRightOnApplicationRole, OperationType.publication, SqlPolicy.Statement.INSERT);
@@ -313,32 +333,24 @@ public class AuthorizationService {
     }
 
     public OreSiUserResult deleteRoleUser(OreSiRoleForUser roleForUser) {
-        if (OreSiRole.superAdmin().equals(roleForUser.getRole())) {
+        if (OreSiRole.superAdmin().getAsSqlRole().equals(roleForUser.getRole())) {
             return deleteAdminRoleUser(roleForUser);
-        } else if (OreSiRole.applicationCreator().equals(roleForUser.getRole())) {
+        } else if (OreSiRole.applicationCreator().getAsSqlRole().equals(roleForUser.getRole())) {
             return deleteApplicationCreatorRoleUser(roleForUser);
         }
-        throw new BadRoleException("cantSetRole", roleForUser.getRole());
+        throw new BadRoleException("cantDeleteRole", roleForUser.getRole());
     }
 
     private OreSiUserResult deleteApplicationCreatorRoleUser(OreSiRoleForUser oreSiUserRoleApplicationCreator) {
-        throw new NoSuchElementException();
-    }
-
-    private OreSiUserResult deleteAdminRoleUser(OreSiRoleForUser oreSiRoleForUserAdmin) {
-        throw new NoSuchElementException();
-    }
-
-    public OreSiUserResult addRoleUser(OreSiRoleForUser roleForUser) {
-        if (OreSiRole.superAdmin().getAsSqlRole().equals(roleForUser.getRole())) {
-            return addAdminRoleUser(roleForUser);
-        } else if (OreSiRole.applicationCreator().getAsSqlRole().equals(roleForUser.getRole())) {
-            return addApplicationCreatorRoleUser(roleForUser);
+        boolean canAddApplicationCreatorRole = canAddApplicationCreatorRole(oreSiUserRoleApplicationCreator);
+        if (canAddApplicationCreatorRole) {
+            final OreSiUser user = authenticationService.deleteUserRightCreateApplication(UUID.fromString(oreSiUserRoleApplicationCreator.getUserId()), oreSiUserRoleApplicationCreator.getApplicationPattern());
+            return new OreSiUserResult(user, userRepository.getRolesForRole(oreSiUserRoleApplicationCreator.getUserId()));
         }
-        throw new BadRoleException("cantSetRole", roleForUser.getRole());
+        throw new NotSuperAdminException();
     }
 
-    private OreSiUserResult addApplicationCreatorRoleUser(OreSiRoleForUser oreSiUserRoleApplicationCreator) {
+    private boolean canAddApplicationCreatorRole(OreSiRoleForUser oreSiUserRoleApplicationCreator) {
         boolean canAddApplicationCreatorRole = false;
         if (authenticationService.hasRole(OreSiRole.superAdmin())) {
             canAddApplicationCreatorRole = true;
@@ -351,6 +363,29 @@ public class AuthorizationService {
             }
 
         }
+        return canAddApplicationCreatorRole;
+    }
+
+    private OreSiUserResult deleteAdminRoleUser(OreSiRoleForUser oreSiRoleForUserAdmin) {
+        boolean canAddsupeadmin = false;
+        if (authenticationService.hasRole(OreSiRole.superAdmin())) {
+            OreSiUser user = authenticationService.deleteUserRightSuperadmin(UUID.fromString(oreSiRoleForUserAdmin.getUserId()));
+            return new OreSiUserResult(user, userRepository.getRolesForRole(oreSiRoleForUserAdmin.getUserId()));
+        }
+        throw new NotSuperAdminException();
+    }
+
+    public OreSiUserResult addRoleUser(OreSiRoleForUser roleForUser) {
+        if (OreSiRole.superAdmin().getAsSqlRole().equals(roleForUser.getRole())) {
+            return addAdminRoleUser(roleForUser);
+        } else if (OreSiRole.applicationCreator().getAsSqlRole().equals(roleForUser.getRole())) {
+            return addApplicationCreatorRoleUser(roleForUser);
+        }
+        throw new BadRoleException("cantSetRole", roleForUser.getRole());
+    }
+
+    private OreSiUserResult addApplicationCreatorRoleUser(OreSiRoleForUser oreSiUserRoleApplicationCreator) {
+        boolean canAddApplicationCreatorRole = canAddApplicationCreatorRole(oreSiUserRoleApplicationCreator);
         if (canAddApplicationCreatorRole) {
             final OreSiUser user = authenticationService.addUserRightCreateApplication(UUID.fromString(oreSiUserRoleApplicationCreator.getUserId()), oreSiUserRoleApplicationCreator.getApplicationPattern());
             return new OreSiUserResult(user, userRepository.getRolesForRole(oreSiUserRoleApplicationCreator.getUserId()));
diff --git a/src/main/java/fr/inra/oresing/rest/OreExceptionHandler.java b/src/main/java/fr/inra/oresing/rest/OreExceptionHandler.java
index 4ac73a870..b1c9fb126 100644
--- a/src/main/java/fr/inra/oresing/rest/OreExceptionHandler.java
+++ b/src/main/java/fr/inra/oresing/rest/OreExceptionHandler.java
@@ -7,8 +7,13 @@ import org.postgresql.util.PSQLException;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.jdbc.BadSqlGrammarException;
+import org.springframework.validation.ObjectError;
 import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
 import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.bind.support.WebExchangeBindException;
+
+import java.util.List;
 
 @RestControllerAdvice
 @Slf4j
@@ -18,6 +23,12 @@ public class OreExceptionHandler {
     public ResponseEntity<String> handle(AuthenticationFailure eee) {
         return ResponseEntity.status(HttpStatus.FORBIDDEN).body(eee.getMessage());
     }
+    @ExceptionHandler
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public List<ObjectError> exception(WebExchangeBindException ex) {
+        log.error("{}", ex.getLocalizedMessage(), ex);
+        return ex.getAllErrors();
+    }
 
     @ExceptionHandler(value = BadSqlGrammarException.class)
     public ResponseEntity<String> handle(BadSqlGrammarException badSqlGrammarException) {
diff --git a/src/main/java/fr/inra/oresing/rest/OreSiService.java b/src/main/java/fr/inra/oresing/rest/OreSiService.java
index 69d90a8b3..cf107febd 100644
--- a/src/main/java/fr/inra/oresing/rest/OreSiService.java
+++ b/src/main/java/fr/inra/oresing/rest/OreSiService.java
@@ -302,12 +302,12 @@ public class OreSiService {
         final String applicationName = app.getName();
         final OreSiUser currentUser = userRepository.findById(request.getRequestClient().getId());
         authenticationService.setRoleForClient();
-        final boolean canCreateApplication = authenticationService.hasRole(OreSiRole.applicationCreator()) &&  currentUser.getAuthorizations().stream()
+        final boolean canCreateApplication = authenticationService.hasRole(OreSiRole.applicationCreator()) && currentUser.getAuthorizations().stream()
                 .anyMatch(s -> applicationName.matches(s));
         final boolean isSuperAdmin = authenticationService.isSuperAdmin();
-        if (!(isSuperAdmin  || canCreateApplication)) {
+        if (!(isSuperAdmin || canCreateApplication)) {
             throw new NotApplicationCreatorRightsException(applicationName, currentUser.getAuthorizations());
-        }else if (!isSuperAdmin) {
+        } else if (!isSuperAdmin) {
             currentUser.getAuthorizations().stream()
                     .filter(s -> isSuperAdmin || applicationName.matches(s))
                     .findAny()
@@ -327,11 +327,11 @@ public class OreSiService {
         app.setConfiguration(configuration);
         try {
             app = initApplication.apply(app);
-            UUID appId = repo.application().store(app);
             UUID confId = storeFile(app, configurationFile, app.getComment());
             app.setConfigFile(confId);
+            UUID appId = repo.application().store(app);
             return appId;
-        } catch (BadSqlGrammarException bsge){
+        } catch (BadSqlGrammarException bsge) {
             throw new NotApplicationCreatorRightsException(applicationName, currentUser.getAuthorizations());
         }
     }
diff --git a/src/main/resources/migration/main/V1__init_schema.sql b/src/main/resources/migration/main/V1__init_schema.sql
index e04e6306c..550c3ee46 100644
--- a/src/main/resources/migration/main/V1__init_schema.sql
+++ b/src/main/resources/migration/main/V1__init_schema.sql
@@ -111,6 +111,8 @@ CREATE ROLE "applicationCreator";
 
 GRANT INSERT, UPDATE ON Application TO "applicationCreator";
 
+GRANT SELECT  ON Application  TO public ;
+
 GRANT SELECT , UPDATE , DELETE ON OreSiUser TO "superadmin", "applicationCreator";
 
 GRANT SELECT, UPDATE, DELETE, REFERENCES ON Application TO "applicationCreator",superadmin;
diff --git a/src/test/java/fr/inra/oresing/rest/AuthorizationResourcesTest.java b/src/test/java/fr/inra/oresing/rest/AuthorizationResourcesTest.java
index db4328f8c..b8c7268fb 100644
--- a/src/test/java/fr/inra/oresing/rest/AuthorizationResourcesTest.java
+++ b/src/test/java/fr/inra/oresing/rest/AuthorizationResourcesTest.java
@@ -8,7 +8,6 @@ import org.apache.commons.lang3.StringUtils;
 import org.hamcrest.Matchers;
 import org.hamcrest.core.IsEqual;
 import org.junit.Assert;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -27,6 +26,7 @@ import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.ResultActions;
 import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.util.NestedServletException;
 
 import javax.servlet.http.Cookie;
 import java.util.Map;
@@ -164,7 +164,6 @@ public class AuthorizationResourcesTest {
 
     @Test
     public void testAddAuthorizationOnTwoScopes() throws Exception {
-
         Cookie authCookie = fixtures.addApplicationHauteFrequence();
 
         CreateUserResult createUserResult = authenticationService.createUser("UnReader", "xxxxxxxx");
@@ -225,8 +224,7 @@ public class AuthorizationResourcesTest {
 
         {
             String json = mockMvc.perform(get("/api/v1/applications/hautefrequence/dataType/hautefrequence/authorization/" + authorizationId)
-                            .cookie(authCookie)
-                            .accept(MediaType.APPLICATION_JSON))
+                            .cookie(authCookie))
                     .andExpect(status().isOk())
                     .andReturn().getResponse().getContentAsString();
 
@@ -274,7 +272,6 @@ public class AuthorizationResourcesTest {
     }
 
     @Test
-    @Ignore
     public void testAddApplicationMonsoere() throws Exception {
         fixtures.addMonsoreApplication();
     }
@@ -350,6 +347,25 @@ public class AuthorizationResourcesTest {
                 //on peut déposer monsore
                 fixtures.createApplicationMonSore(applicationCreatorCookies, "monsore");
             }
+            {
+                //on supprime des droits pour le pattern monsore
+                ResultActions resultActions = mockMvc.perform(delete("/api/v1/authorization/applicationCreator")
+                                .param("userId", applicationCreatorResult.getUserId().toString())
+                                .param("applicationPattern", "monsore")
+                                .cookie(dbUserCookies))
+                        .andExpect(status().is2xxSuccessful())
+                        .andExpect(jsonPath("$.roles.currentUser", IsEqual.equalTo(applicationCreatorResult.getUserId().toString())))
+                        .andExpect(jsonPath("$.roles.memberOf", not(Matchers.hasItem("applicationCreator"))))
+                        .andExpect(jsonPath("$.authorizations", not(Matchers.hasItem("monsore"))))
+                        .andExpect(jsonPath("$.id", IsEqual.equalTo(applicationCreatorResult.getUserId().toString())));
+
+                //on ne peut déposer monsore
+                try {
+                    fixtures.createApplicationMonSore(applicationCreatorCookies, "monsore");
+                }catch (NotApplicationCreatorRightsException e){
+                    Assert.assertEquals(("monsore", e.applicationName);
+                }
+            }
         }
 
     }
diff --git a/src/test/java/fr/inra/oresing/rest/Fixtures.java b/src/test/java/fr/inra/oresing/rest/Fixtures.java
index 155705f6f..6e112612a 100644
--- a/src/test/java/fr/inra/oresing/rest/Fixtures.java
+++ b/src/test/java/fr/inra/oresing/rest/Fixtures.java
@@ -7,15 +7,21 @@ import com.google.common.io.Resources;
 import fr.inra.oresing.OreSiTechnicalException;
 import fr.inra.oresing.model.OreSiUser;
 import fr.inra.oresing.persistence.AuthenticationService;
+import fr.inra.oresing.persistence.UserRepository;
 import org.apache.commons.io.IOUtils;
+import org.hamcrest.Matchers;
+import org.hamcrest.core.IsEqual;
 import org.junit.Assert;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
+import org.springframework.mock.web.MockHttpServletResponse;
 import org.springframework.mock.web.MockMultipartFile;
 import org.springframework.stereotype.Component;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.ResultActions;
 import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
 import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.util.NestedServletException;
 
 import javax.servlet.http.Cookie;
@@ -28,11 +34,14 @@ import java.time.temporal.ChronoUnit;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.Map;
+import java.util.UUID;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 
 @Component
@@ -43,6 +52,11 @@ public class Fixtures {
 
     @Autowired
     private AuthenticationService authenticationService;
+    @Autowired
+    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
+
+    @Autowired
+    private UserRepository userRepository;
     private Cookie cookie;
 
     public String getMonsoreApplicationName() {
@@ -85,12 +99,12 @@ public class Fixtures {
         //fougeres-fou_4_swc_j_01-01-1999_31-01-1999.csv
         final Pattern pattern = Pattern.compile("(.*)_" + datatype + "_(.*)_(.*).csv");
         final Matcher matcher = pattern.matcher(fileName);
-        if(!matcher.matches()){
+        if (!matcher.matches()) {
             return null;
         }
         String zone_etude = matcher.group(1);
         final String[] parent_site = zone_etude.split("-");
-        if(parent_site.length>1){
+        if (parent_site.length > 1) {
             zone_etude = String.format("%1$s.%1$s__%2$s", parent_site[0], parent_site[1]);
         }
         final DateTimeFormatter formaterIn = DateTimeFormatter.ofPattern("dd-MM-yyyy");
@@ -98,8 +112,8 @@ public class Fixtures {
 
         final boolean isMonthly = datatype.matches(".*_m");
         final String format = (isMonthly ? "01-" : "") + "%s";
-        String dateDebut =formaterOut.format(LocalDate.parse(String.format(format, matcher.group(2)), formaterIn).atStartOfDay(ZoneOffset.UTC))+" 00:00:00";
-        String dateFin =formaterOut.format(LocalDate.parse(String.format(format, matcher.group(3)), formaterIn).atTime(0,0).plus(1, isMonthly ? ChronoUnit.MONTHS:ChronoUnit.DAYS))+" 00:00:00";
+        String dateDebut = formaterOut.format(LocalDate.parse(String.format(format, matcher.group(2)), formaterIn).atStartOfDay(ZoneOffset.UTC)) + " 00:00:00";
+        String dateFin = formaterOut.format(LocalDate.parse(String.format(format, matcher.group(3)), formaterIn).atTime(0, 0).plus(1, isMonthly ? ChronoUnit.MONTHS : ChronoUnit.DAYS)) + " 00:00:00";
         return String.format("{\n" +
                 "   \"fileid\":null,\n" +
                 "   \"binaryfiledataset\":{\n" +
@@ -266,43 +280,68 @@ public class Fixtures {
         return cookie;
     }
 
+    @Transactional
+    void addRoleAdmin(CreateUserResult dbUserResult) {
+        namedParameterJdbcTemplate.update("grant \"superadmin\" to \"" + dbUserResult.getUserId().toString() + "\"", Map.of());
+    }
+
     public Cookie addApplicationCreatorUser(String applicationPattern) throws Exception {
         if (cookie == null) {
             String aPassword = "xxxxxxxx";
             String aLogin = "poussin";
             CreateUserResult createUserResult = authenticationService.createUser(aLogin, aPassword);
-            final OreSiUser user = authenticationService.addUserRightCreateApplication(createUserResult.getUserId(), applicationPattern);
-            Assert.assertTrue(user.getAuthorizations().contains(applicationPattern));
-            cookie = mockMvc.perform(post("/api/v1/login")
+            addRoleAdmin(createUserResult);
+            final MockHttpServletResponse response = mockMvc.perform(post("/api/v1/login")
                             .param("login", aLogin)
                             .param("password", aPassword))
-                    .andReturn().getResponse().getCookie(AuthHelper.JWT_COOKIE_NAME);
-        }
-        return cookie;
-    }
-    public String createApplicationMonSore(Cookie authCookie, String applicationName) throws Exception{
+                    .andReturn().getResponse();
+            cookie = response.getCookie(AuthHelper.JWT_COOKIE_NAME);
+        }
+        String aPassword = "xxxxxxxx";
+        String aLogin = applicationPattern;
+        CreateUserResult createUserResult = authenticationService.createUser(aLogin, aPassword);
+        final UUID userId = createUserResult.getUserId();
+        ResultActions resultActions = mockMvc.perform(put("/api/v1/authorization/applicationCreator")
+                        .param("userId", userId.toString())
+                        .param("applicationPattern", applicationPattern)
+                        .cookie(cookie))
+                .andExpect(status().is2xxSuccessful())
+                .andExpect(jsonPath("$.roles.currentUser", IsEqual.equalTo(userId.toString())))
+                .andExpect(jsonPath("$.roles.memberOf", Matchers.hasItem("applicationCreator")))
+                .andExpect(jsonPath("$.authorizations", Matchers.hasItem(applicationPattern)))
+                .andExpect(jsonPath("$.id", IsEqual.equalTo(userId.toString())));
+        final OreSiUser user = userRepository.findById(userId);
+        Assert.assertTrue(user.getAuthorizations().contains(applicationPattern));
+        Cookie applicationCreator = mockMvc.perform(post("/api/v1/login")
+                        .param("login", aLogin)
+                        .param("password", aPassword))
+                .andReturn().getResponse().getCookie(AuthHelper.JWT_COOKIE_NAME);
+        return applicationCreator;
+    }
+
+    public String createApplicationMonSore(Cookie authCookie, String applicationName) throws Exception {
         ResultActions resultActions = null;
         try (InputStream configurationFile = getClass().getResourceAsStream(getMonsoreApplicationConfigurationResourceName())) {
             MockMultipartFile configuration = new MockMultipartFile("file", "monsore.yaml", "text/plain", configurationFile);
-            resultActions = mockMvc.perform(MockMvcRequestBuilders.multipart(String.format("/api/v1/applications/%s", applicationName==null?"monsore" : applicationName))
+            resultActions = mockMvc.perform(MockMvcRequestBuilders.multipart(String.format("/api/v1/applications/%s", applicationName == null ? "monsore" : applicationName))
                     .file(configuration)
                     .cookie(authCookie));
-                    return resultActions.andExpect(MockMvcResultMatchers.status().isCreated())
-                            .andReturn()
-                            .getResponse().getContentAsString();
-        }catch (NestedServletException e){
-            if(e.getCause() instanceof NotApplicationCreatorRightsException) {
+            return resultActions.andExpect(MockMvcResultMatchers.status().isCreated())
+                    .andReturn()
+                    .getResponse().getContentAsString();
+        } catch (NestedServletException e) {
+            if (e.getCause() instanceof NotApplicationCreatorRightsException) {
                 throw (NotApplicationCreatorRightsException) e.getCause();
             }
             throw e;
-        }catch (AssertionError e){
+        } catch (AssertionError e) {
             return resultActions.andReturn().getResolvedException().getMessage();
         }
     }
 
     public Cookie addMonsoreApplication() throws Exception {
         Cookie authCookie = addApplicationCreatorUser("monsore");
-        String result = createApplicationMonSore(cookie, "monsore");
+        String result = createApplicationMonSore(authCookie, "monsore");
 
         // Ajout de referentiel
         for (Map.Entry<String, String> e : getMonsoreReferentielFiles().entrySet()) {
diff --git a/src/test/java/fr/inra/oresing/rest/OreSiResourcesTest.java b/src/test/java/fr/inra/oresing/rest/OreSiResourcesTest.java
index 290b75efa..0eeb0ce52 100644
--- a/src/test/java/fr/inra/oresing/rest/OreSiResourcesTest.java
+++ b/src/test/java/fr/inra/oresing/rest/OreSiResourcesTest.java
@@ -10,6 +10,7 @@ import fr.inra.oresing.ValidationLevel;
 import fr.inra.oresing.checker.InvalidDatasetContentException;
 import fr.inra.oresing.persistence.AuthenticationService;
 import fr.inra.oresing.persistence.JsonRowMapper;
+import fr.inra.oresing.persistence.UserRepository;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -92,6 +93,8 @@ public class OreSiResourcesTest {
 
     @Autowired
     private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
+    @Autowired
+    private UserRepository userRepository;
 
 
     @Before
@@ -135,7 +138,7 @@ public class OreSiResourcesTest {
                         .andExpect(status().is4xxClientError());
                 addUserRightCreateApplication(monsoreUserId, "monsore");
                 Assert.fail();
-            }catch (NestedServletException e){
+            } catch (NestedServletException e) {
                 final NotApplicationCreatorRightsException cause = (NotApplicationCreatorRightsException) e.getCause();
                 Assert.assertEquals("monsore", cause.applicationName);
             }
@@ -624,7 +627,7 @@ public class OreSiResourcesTest {
      */
     @Test
     public void testProgressiveYamlWithoutAuthorization() throws Exception {
-
+        String authorizationId;
         URL resource = getClass().getResource(fixtures.getProgressiveYaml().get("yamlWithoutAuthorization"));
         try (InputStream in = Objects.requireNonNull(resource).openStream()) {
             MockMultipartFile configuration = new MockMultipartFile("file", "progressive.yaml", "text/plain", in);
@@ -641,7 +644,7 @@ public class OreSiResourcesTest {
         progressiveYamlAddData();
 
         String lambdaUserId = lambdaUser.getUserId().toString();
-        Cookie authReaderCookie = mockMvc.perform(post("/api/v1/login")
+        Cookie readerCookies = mockMvc.perform(post("/api/v1/login")
                         .param("login", "lambda")
                         .param("password", "xxxxxxxx"))
                 .andReturn().getResponse().getCookie(AuthHelper.JWT_COOKIE_NAME);
@@ -649,14 +652,14 @@ public class OreSiResourcesTest {
 
         {
             String response = mockMvc.perform(get("/api/v1/applications")
-                    .cookie(authReaderCookie)
+                    .cookie(readerCookies)
             ).andReturn().getResponse().getContentAsString();
             Assert.assertFalse("On ne devrait pas voir l'application car les droits n'ont pas encore été accordés", response.contains("progressive"));
         }
 
         {
             mockMvc.perform(get("/api/v1/applications/progressive/data/date_de_visite")
-                            .cookie(authReaderCookie)
+                            .cookie(readerCookies)
                             .accept(MediaType.TEXT_PLAIN))
                     .andExpect(status().is4xxClientError());
         }
@@ -693,31 +696,59 @@ public class OreSiResourcesTest {
                     "}\n" +
                     "}";
 
+            // L'utilisateur sans droit ne peut voir les applications
+            String response = mockMvc.perform(get("/api/v1/applications")
+                            .cookie(readerCookies)
+                    )
+                    .andExpect(jsonPath("$[*].name", Matchers.hasSize(0)))
+                    .andReturn().getResponse().getContentAsString();
+
+
             MockHttpServletRequestBuilder create = post("/api/v1/applications/progressive/dataType/date_de_visite/authorization")
                     .contentType(MediaType.APPLICATION_JSON)
                     .cookie(authCookie)
                     .content(json);
-            String response = mockMvc.perform(create)
+            response = mockMvc.perform(create)
                     .andExpect(status().isCreated())
                     .andReturn().getResponse().getContentAsString();
+            authorizationId = JsonPath.parse(response).read("$.authorizationId", String.class);
             log.debug(StringUtils.abbreviate(response, 50));
         }
 
         {
+            // Une fois l'accès donné, on doit pouvoir avec l'application dans la liste"
             String response = mockMvc.perform(get("/api/v1/applications")
-                    .cookie(authReaderCookie)
-            ).andReturn().getResponse().getContentAsString();
-            Assert.assertTrue("Une fois l'accès donné, on doit pouvoir avec l'application dans la liste", response.contains("progressive"));
+                            .cookie(readerCookies)
+                    )
+                    .andExpect(jsonPath("$[*].name", Matchers.hasSize(1)))
+                    .andExpect(jsonPath("$[*].name", Matchers.contains("progressive")))
+                    .andReturn().getResponse().getContentAsString();
         }
 
         {
             String json = mockMvc.perform(get("/api/v1/applications/progressive/data/date_de_visite")
-                            .cookie(authReaderCookie)
+                            .cookie(readerCookies)
                             .accept(MediaType.APPLICATION_JSON))
                     .andExpect(status().isOk())
                     .andExpect(jsonPath("$.rows[*].values.relevant.numero").value(hasItemInArray(equalTo("125")), String[].class))
                     .andReturn().getResponse().getContentAsString();
         }
+            MockHttpServletRequestBuilder delete = delete(String.format("/api/v1/applications/progressive/dataType/date_de_visite/authorization/%s", authorizationId))
+                    .contentType(MediaType.APPLICATION_JSON)
+                    .cookie(authCookie);
+             mockMvc.perform(delete)
+                    .andExpect(status().is2xxSuccessful())
+                    .andReturn().getResponse().getContentAsString();
+            // L'utilisateur sans droit ne peut voir les applications
+
+        //TODO
+
+            /*String response = mockMvc.perform(get("/api/v1/applications")
+                            .cookie(readerCookies)
+                    )
+                    .andExpect(jsonPath("$[*].name", Matchers.hasSize(0)))
+                    .andReturn().getResponse().getContentAsString();*/
+
 
     }
 
-- 
GitLab


From 0d46cc3c2fa7e19737d87e060a070c8a9a787270 Mon Sep 17 00:00:00 2001
From: TCHERNIATINSKY <philippe.tcherniatinsky@inrae.fr>
Date: Fri, 6 May 2022 18:07:21 +0200
Subject: [PATCH 3/3] Oups !

---
 .../java/fr/inra/oresing/rest/AuthorizationResourcesTest.java  | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/test/java/fr/inra/oresing/rest/AuthorizationResourcesTest.java b/src/test/java/fr/inra/oresing/rest/AuthorizationResourcesTest.java
index b8c7268fb..2352ca56b 100644
--- a/src/test/java/fr/inra/oresing/rest/AuthorizationResourcesTest.java
+++ b/src/test/java/fr/inra/oresing/rest/AuthorizationResourcesTest.java
@@ -26,7 +26,6 @@ import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.ResultActions;
 import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
 import org.springframework.transaction.annotation.Transactional;
-import org.springframework.web.util.NestedServletException;
 
 import javax.servlet.http.Cookie;
 import java.util.Map;
@@ -363,7 +362,7 @@ public class AuthorizationResourcesTest {
                 try {
                     fixtures.createApplicationMonSore(applicationCreatorCookies, "monsore");
                 }catch (NotApplicationCreatorRightsException e){
-                    Assert.assertEquals(("monsore", e.applicationName);
+                    Assert.assertEquals("monsore", e.applicationName);
                 }
             }
         }
-- 
GitLab