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