diff --git a/gms-client/gms-cli/src/main/java/it/inaf/ia2/gms/cli/CLI.java b/gms-client/gms-cli/src/main/java/it/inaf/ia2/gms/cli/CLI.java index 356bfa3baca80f0e6d17924a23f5f663c0d41c3a..b183e2ca2f26a53354ff11ea0b95eed81ba0fd88 100644 --- a/gms-client/gms-cli/src/main/java/it/inaf/ia2/gms/cli/CLI.java +++ b/gms-client/gms-cli/src/main/java/it/inaf/ia2/gms/cli/CLI.java @@ -93,13 +93,6 @@ public class CLI implements CommandLineRunner { client.removePermission(getNames(args, 1, args.length - 2), args[args.length - 1]); System.out.println("Permission removed"); break; - case "prepare-join": - if (args.length != 3) { - displayUsage(); - } - client.prepareToJoin(args[1], args[2]); - System.out.println("Join prepared"); - break; default: displayUsage(); break; @@ -113,8 +106,7 @@ public class CLI implements CommandLineRunner { + " add-member <name1 name2 name3> <user_id>\n" + " remove-member <name1 name2 name3> <user_id>\n" + " add-permission <name1 name2 name3> <user_id> <permission>\n" - + " delete-permission <name1 name2 name3> <user_id>\n" - + " prepare-join <from_user_id> <to_user_id>"); + + " delete-permission <name1 name2 name3> <user_id>"); System.exit(0); } diff --git a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/GmsClient.java b/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/GmsClient.java index d788dd159f127ce05148c42a42eaba848240e6ba..ab11ceec1e47a2d8d5b0eb0157df9966cfe7a5b0 100644 --- a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/GmsClient.java +++ b/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/GmsClient.java @@ -116,20 +116,6 @@ public class GmsClient { restTemplate.exchange(url, HttpMethod.DELETE, getEntity(), Void.class); } - public void prepareToJoin(String fromUserId, String toUserId) { - - String url = UriComponentsBuilder.fromHttpUrl(baseUrl) - .pathSegment("prepare-join") - .toUriString(); - - Map<String, Object> params = new HashMap<>(); - params.put("fromUserId", fromUserId); - params.put("toUserId", toUserId); - HttpEntity<Map<String, Object>> httpEntity = getEntity(params); - - restTemplate.exchange(url, HttpMethod.POST, httpEntity, Void.class); - } - private HttpEntity<?> getEntity() { return new HttpEntity<>(getHeaders()); } diff --git a/gms-client/gms-client-lib/src/test/java/it/inaf/ia2/gms/client/GmsClientTest.java b/gms-client/gms-client-lib/src/test/java/it/inaf/ia2/gms/client/GmsClientTest.java index 7771a98d5cae520b6c2c8927fa535a498a6b8505..e0eeb8d699633e669f776f791d3647ed31db8609 100644 --- a/gms-client/gms-client-lib/src/test/java/it/inaf/ia2/gms/client/GmsClientTest.java +++ b/gms-client/gms-client-lib/src/test/java/it/inaf/ia2/gms/client/GmsClientTest.java @@ -150,28 +150,6 @@ public class GmsClientTest { verifyAuthHeaders(entity); } - @Test - public void testPrepareToJoin() { - - String fromUserId = "from_user_id"; - String toUserId = "to_user_id"; - - client.prepareToJoin(fromUserId, toUserId); - - ArgumentCaptor<HttpEntity> entityCaptor = ArgumentCaptor.forClass(HttpEntity.class); - verify(restTemplate, times(1)).exchange(eq(BASE_URL + "/ws/prepare-join"), - eq(HttpMethod.POST), entityCaptor.capture(), eq(Void.class)); - - HttpEntity<?> entity = entityCaptor.getValue(); - verifyAuthHeaders(entity); - - Map<String, Object> expectedBody = new HashMap<>(); - expectedBody.put("fromUserId", fromUserId); - expectedBody.put("toUserId", toUserId); - - verifyBody(entity, expectedBody); - } - private void verifyAuthHeaders(HttpEntity<?> entity) {// String authHeader = entity.getHeaders().getFirst("Authorization"); assertEquals("Basic dGVzdDp0ZXN0", authHeader); diff --git a/gms/src/main/java/it/inaf/ia2/gms/GmsApplication.java b/gms/src/main/java/it/inaf/ia2/gms/GmsApplication.java index 583372471d738a43a2dd4ba8892a45e1ec35faac..a3b2a8f4c3c586824ecaad0ed62fb075ac3a8532 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/GmsApplication.java +++ b/gms/src/main/java/it/inaf/ia2/gms/GmsApplication.java @@ -2,8 +2,12 @@ package it.inaf.ia2.gms; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.annotation.EnableTransactionManagement; @SpringBootApplication +@Configuration +@EnableTransactionManagement public class GmsApplication { public static void main(String[] args) { diff --git a/gms/src/main/java/it/inaf/ia2/gms/authn/JWTFilter.java b/gms/src/main/java/it/inaf/ia2/gms/authn/JWTFilter.java index 88b44cac02dcebd704a7321c83042482b8a7d974..5310b245d5834b2050354b1d90034c4d348fc628 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/authn/JWTFilter.java +++ b/gms/src/main/java/it/inaf/ia2/gms/authn/JWTFilter.java @@ -11,7 +11,6 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.provider.token.store.jwk.JwkTokenStore; @@ -45,29 +44,28 @@ public class JWTFilter implements Filter { Map<String, Object> claims = accessToken.getAdditionalInformation(); - String principal = (String) claims.get("sub"); - if (principal == null) { + if (claims.get("sub") == null) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid access token: missing sub claim"); return; } - ServletRequest wrappedRequest = new ServletRequestWithJWTPrincipal(request, principal); + ServletRequest wrappedRequest = new ServletRequestWithJWTPrincipal(request, claims); fc.doFilter(wrappedRequest, res); } private static class ServletRequestWithJWTPrincipal extends HttpServletRequestWrapper { - private final String principal; + private final Principal principal; - public ServletRequestWithJWTPrincipal(HttpServletRequest request, String principal) { + public ServletRequestWithJWTPrincipal(HttpServletRequest request, Map<String, Object> jwtClaims) { super(request); - this.principal = principal; + this.principal = new RapPrincipal(jwtClaims); } @Override public Principal getUserPrincipal() { - return new UsernamePasswordAuthenticationToken(principal, null); + return principal; } } } diff --git a/gms/src/main/java/it/inaf/ia2/gms/authn/RapPrincipal.java b/gms/src/main/java/it/inaf/ia2/gms/authn/RapPrincipal.java new file mode 100644 index 0000000000000000000000000000000000000000..174ff2f114a4752cc45277a8c33d70845d71bf98 --- /dev/null +++ b/gms/src/main/java/it/inaf/ia2/gms/authn/RapPrincipal.java @@ -0,0 +1,27 @@ +package it.inaf.ia2.gms.authn; + +import java.security.Principal; +import java.util.Map; + +public class RapPrincipal implements Principal { + + private final String sub; + private final String altSub; + + public RapPrincipal(Map<String, Object> jwtClaims) { + sub = (String) jwtClaims.get("sub"); + altSub = (String) jwtClaims.get("alt_sub"); + } + + @Override + public String getName() { + return sub; + } + + /** + * Alternative subject identifier: used during a join. + */ + public String getAlternativeName() { + return altSub; + } +} diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/BasicAuthWebServiceController.java b/gms/src/main/java/it/inaf/ia2/gms/controller/BasicAuthWebServiceController.java index 49530ba843ce72651a817ac6ca5a477828775c4a..9b9d27fa478d9a7040a90e32d07b5b1844ff32e2 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/controller/BasicAuthWebServiceController.java +++ b/gms/src/main/java/it/inaf/ia2/gms/controller/BasicAuthWebServiceController.java @@ -3,7 +3,6 @@ package it.inaf.ia2.gms.controller; import it.inaf.ia2.gms.exception.BadRequestException; import it.inaf.ia2.gms.model.request.AddMemberWsRequest; import it.inaf.ia2.gms.model.request.AddPermissionWsRequest; -import it.inaf.ia2.gms.model.PrepareToJoinRequest; import it.inaf.ia2.gms.persistence.model.GroupEntity; import it.inaf.ia2.gms.persistence.model.MembershipEntity; import it.inaf.ia2.gms.persistence.model.PermissionEntity; @@ -112,15 +111,6 @@ public class BasicAuthWebServiceController { return ResponseEntity.noContent().build(); } - @PostMapping(value = "/prepare-join", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public ResponseEntity<?> prepareToJoin(@Valid @RequestBody PrepareToJoinRequest request) { - - permissionsService.movePermissions(request.getFromUserId(), request.getToUserId()); - membersService.moveMemberships(request.getFromUserId(), request.getToUserId()); - - return ResponseEntity.ok().build(); - } - private GroupEntity getGroupByNames(List<String> names) { return groupsService.findGroupByNames(names) .orElseThrow(() -> new BadRequestException("Unable to find requested group")); diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/JWTWebServiceController.java b/gms/src/main/java/it/inaf/ia2/gms/controller/JWTWebServiceController.java index 53f2d75d218cb73ed10e989556d4e4e0b362a1d2..4f632772d2a040ef538775e086e0272136c574b8 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/controller/JWTWebServiceController.java +++ b/gms/src/main/java/it/inaf/ia2/gms/controller/JWTWebServiceController.java @@ -1,8 +1,11 @@ package it.inaf.ia2.gms.controller; +import it.inaf.ia2.gms.authn.RapPrincipal; +import it.inaf.ia2.gms.exception.BadRequestException; import it.inaf.ia2.gms.persistence.GroupsDAO; import it.inaf.ia2.gms.persistence.MembershipsDAO; import it.inaf.ia2.gms.persistence.model.GroupEntity; +import it.inaf.ia2.gms.service.JoinService; import java.io.IOException; import java.io.PrintWriter; import java.security.Principal; @@ -15,7 +18,9 @@ import java.util.Set; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -29,6 +34,9 @@ public class JWTWebServiceController { @Autowired private MembershipsDAO membershipsDAO; + @Autowired + private JoinService joinService; + @Autowired private GroupsDAO groupsDAO; @@ -93,4 +101,21 @@ public class JWTWebServiceController { return String.join(".", names); } + + @PostMapping(value = "/join", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity<?> join(RapPrincipal principal) { + + String fromUser = principal.getName(); + String toUser = principal.getAlternativeName(); + + if (toUser == null) { + throw new BadRequestException("Missing alternative subject"); + } + + joinService.join(fromUser, toUser); + + Map<String, String> responseBody = new HashMap<>(); + responseBody.put("mergedId", fromUser); + return ResponseEntity.ok(responseBody); + } } diff --git a/gms/src/main/java/it/inaf/ia2/gms/model/PrepareToJoinRequest.java b/gms/src/main/java/it/inaf/ia2/gms/model/PrepareToJoinRequest.java deleted file mode 100644 index 84681f784d582bf81f4396210611c409d8b566a6..0000000000000000000000000000000000000000 --- a/gms/src/main/java/it/inaf/ia2/gms/model/PrepareToJoinRequest.java +++ /dev/null @@ -1,23 +0,0 @@ -package it.inaf.ia2.gms.model; - -public class PrepareToJoinRequest { - - private String fromUserId; - private String toUserId; - - public String getFromUserId() { - return fromUserId; - } - - public void setFromUserId(String fromUserId) { - this.fromUserId = fromUserId; - } - - public String getToUserId() { - return toUserId; - } - - public void setToUserId(String toUserId) { - this.toUserId = toUserId; - } -} diff --git a/gms/src/main/java/it/inaf/ia2/gms/persistence/JoinDAO.java b/gms/src/main/java/it/inaf/ia2/gms/persistence/JoinDAO.java new file mode 100644 index 0000000000000000000000000000000000000000..a62f315af2e4cb06bc1c86b2031be0ae0b6a56ab --- /dev/null +++ b/gms/src/main/java/it/inaf/ia2/gms/persistence/JoinDAO.java @@ -0,0 +1,107 @@ +package it.inaf.ia2.gms.persistence; + +import it.inaf.ia2.gms.persistence.model.MembershipEntity; +import it.inaf.ia2.gms.persistence.model.PermissionEntity; +import java.sql.PreparedStatement; +import java.sql.Types; +import java.util.Collections; +import java.util.Set; +import javax.sql.DataSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +@Component +public class JoinDAO { + + private static final Logger LOG = LoggerFactory.getLogger(JoinDAO.class); + + private final JdbcTemplate jdbcTemplate; + + @Autowired + public JoinDAO(DataSource dataSource) { + jdbcTemplate = new JdbcTemplate(dataSource); + } + + @Transactional + public void join(Set<MembershipEntity> membershipsToAdd, Set<PermissionEntity> permissionsToAdd, String userToDelete) { + + if (!membershipsToAdd.isEmpty()) { + addMemberships(membershipsToAdd); + } + if (!permissionsToAdd.isEmpty()) { + addPermissions(permissionsToAdd); + } + deleteUserMemberships(userToDelete); + deleteUserPermissions(userToDelete); + } + + private void addMemberships(Set<MembershipEntity> membershipsToAdd) { + + String sql = "INSERT INTO gms_membership (group_id, user_id) VALUES " + + String.join(", ", Collections.nCopies(membershipsToAdd.size(), "(?, ?)")); + + LOG.trace("Executing {}", sql); + + jdbcTemplate.update(conn -> { + PreparedStatement ps = conn.prepareStatement(sql); + int i = 0; + for (MembershipEntity membership : membershipsToAdd) { + ps.setString(++i, membership.getGroupId()); + ps.setString(++i, membership.getUserId()); + } + return ps; + }); + } + + private void addPermissions(Set<PermissionEntity> permissionsToAdd) { + + String sql = "INSERT INTO gms_permission (group_id, user_id, permission, group_path) VALUES " + + String.join(", ", Collections.nCopies(permissionsToAdd.size(), "(?, ?, ?, ?)")) + "\n" + + "ON CONFLICT (group_id, user_id) DO UPDATE\n" + + "SET permission = EXCLUDED.permission";; + + LOG.trace("Executing {}", sql); + + jdbcTemplate.update(conn -> { + PreparedStatement ps = conn.prepareStatement(sql); + int i = 0; + for (PermissionEntity permission : permissionsToAdd) { + ps.setString(++i, permission.getGroupId()); + ps.setString(++i, permission.getUserId()); + ps.setObject(++i, permission.getPermission().toString(), Types.OTHER); + ps.setObject(++i, permission.getGroupPath(), Types.OTHER); + } + return ps; + }); + } + + private void deleteUserMemberships(String userId) { + + String sql = "DELETE FROM gms_membership WHERE user_id = ?"; + + LOG.trace("Executing {}", sql); + + jdbcTemplate.update(conn -> { + PreparedStatement ps = conn.prepareStatement(sql); + ps.setString(1, userId); + return ps; + }); + } + + private void deleteUserPermissions(String userId) { + + String sql = "DELETE FROM gms_permission WHERE user_id = ?"; + + LOG.trace("Executing {}", sql); + + jdbcTemplate.update(conn -> { + PreparedStatement ps = conn.prepareStatement(sql); + ps.setString(1, userId); + return ps; + }); + } +} diff --git a/gms/src/main/java/it/inaf/ia2/gms/persistence/MembershipsDAO.java b/gms/src/main/java/it/inaf/ia2/gms/persistence/MembershipsDAO.java index 1d31707f83a292d0d1f6117cdccf21536aab7ebe..d8ff1abdffaad824e08f5a7e2384bd9dc4b7fabe 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/persistence/MembershipsDAO.java +++ b/gms/src/main/java/it/inaf/ia2/gms/persistence/MembershipsDAO.java @@ -109,18 +109,6 @@ public class MembershipsDAO { }); } - public void moveMemberships(String fromUserId, String toUserId) { - - String sql = "UPDATE gms_membership SET user_id = ? WHERE user_id = ?"; - - jdbcTemplate.update(conn -> { - PreparedStatement ps = conn.prepareStatement(sql); - ps.setString(1, toUserId); - ps.setString(2, fromUserId); - return ps; - }); - } - public void deleteAllGroupsMembership(List<String> groupIds) { String sql = "DELETE FROM gms_membership WHERE group_id IN (" diff --git a/gms/src/main/java/it/inaf/ia2/gms/persistence/PermissionsDAO.java b/gms/src/main/java/it/inaf/ia2/gms/persistence/PermissionsDAO.java index 07c277471e8a84df939ab1eaa0ffda650597e565..d8d417ee24f340f86bfe14111e3f7fe8f4a03a13 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/persistence/PermissionsDAO.java +++ b/gms/src/main/java/it/inaf/ia2/gms/persistence/PermissionsDAO.java @@ -144,18 +144,6 @@ public class PermissionsDAO { }); } - public void movePermissions(String fromUserId, String toUserId) { - - String sql = "UPDATE gms_permission SET user_id = ? WHERE user_id = ?"; - - jdbcTemplate.update(conn -> { - PreparedStatement ps = conn.prepareStatement(sql); - ps.setString(1, toUserId); - ps.setString(2, fromUserId); - return ps; - }); - } - public void deleteAllGroupsPermissions(List<String> groupIds) { String sql = "DELETE FROM gms_permission WHERE group_id IN (" diff --git a/gms/src/main/java/it/inaf/ia2/gms/persistence/model/MembershipEntity.java b/gms/src/main/java/it/inaf/ia2/gms/persistence/model/MembershipEntity.java index 81c793c7c3c46fe2162f204a6558011eb7a02460..c71bccb32664a9f6a3b412926e592b146c48345a 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/persistence/model/MembershipEntity.java +++ b/gms/src/main/java/it/inaf/ia2/gms/persistence/model/MembershipEntity.java @@ -1,5 +1,6 @@ package it.inaf.ia2.gms.persistence.model; +import java.util.Objects; import javax.validation.constraints.NotEmpty; public class MembershipEntity { @@ -24,4 +25,30 @@ public class MembershipEntity { public void setUserId(String userId) { this.userId = userId; } + + @Override + public int hashCode() { + int hash = 5; + hash = 67 * hash + Objects.hashCode(this.groupId); + hash = 67 * hash + Objects.hashCode(this.userId); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final MembershipEntity other = (MembershipEntity) obj; + if (!Objects.equals(this.groupId, other.groupId)) { + return false; + } + return Objects.equals(this.userId, other.userId); + } } diff --git a/gms/src/main/java/it/inaf/ia2/gms/persistence/model/PermissionEntity.java b/gms/src/main/java/it/inaf/ia2/gms/persistence/model/PermissionEntity.java index 98bfc6f744a440b493b3c370b4f18b0e0467f0d7..ca6c623192dfa4fa54ae015528ddee2d1920bd59 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/persistence/model/PermissionEntity.java +++ b/gms/src/main/java/it/inaf/ia2/gms/persistence/model/PermissionEntity.java @@ -1,6 +1,7 @@ package it.inaf.ia2.gms.persistence.model; import it.inaf.ia2.gms.model.Permission; +import java.util.Objects; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; @@ -47,4 +48,38 @@ public class PermissionEntity { public void setGroupPath(String groupPath) { this.groupPath = groupPath; } + + @Override + public int hashCode() { + int hash = 5; + hash = 41 * hash + Objects.hashCode(this.userId); + hash = 41 * hash + Objects.hashCode(this.groupId); + hash = 41 * hash + Objects.hashCode(this.permission); + hash = 41 * hash + Objects.hashCode(this.groupPath); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final PermissionEntity other = (PermissionEntity) obj; + if (!Objects.equals(this.userId, other.userId)) { + return false; + } + if (!Objects.equals(this.groupId, other.groupId)) { + return false; + } + if (!Objects.equals(this.groupPath, other.groupPath)) { + return false; + } + return this.permission == other.permission; + } } diff --git a/gms/src/main/java/it/inaf/ia2/gms/service/JoinService.java b/gms/src/main/java/it/inaf/ia2/gms/service/JoinService.java new file mode 100644 index 0000000000000000000000000000000000000000..ebad2a8b0b1e0d7d43cf88b48d3697cadd283aef --- /dev/null +++ b/gms/src/main/java/it/inaf/ia2/gms/service/JoinService.java @@ -0,0 +1,76 @@ +package it.inaf.ia2.gms.service; + +import it.inaf.ia2.gms.model.Permission; +import it.inaf.ia2.gms.persistence.JoinDAO; +import it.inaf.ia2.gms.persistence.MembershipsDAO; +import it.inaf.ia2.gms.persistence.PermissionsDAO; +import it.inaf.ia2.gms.persistence.model.MembershipEntity; +import it.inaf.ia2.gms.persistence.model.PermissionEntity; +import java.util.Set; +import java.util.stream.Collectors; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class JoinService { + + @Autowired + private MembershipsDAO membershipsDAO; + + @Autowired + private PermissionsDAO permissionsDAO; + + @Autowired + private JoinDAO joinDAO; + + public void join(String userId1, String userId2) { + + Set<MembershipEntity> existingMemberships + = membershipsDAO.getUserMemberships(userId1).stream() + .map(g -> getMembershipEntity(g.getId(), userId1)) + .collect(Collectors.toSet()); + + Set<MembershipEntity> membershipsToAdd + = membershipsDAO.getUserMemberships(userId2).stream() + .map(g -> getMembershipEntity(g.getId(), userId1)) + .filter(m -> !existingMemberships.contains(m)) + .collect(Collectors.toSet()); + + Set<PermissionEntity> existingPermissions + = permissionsDAO.findUserPermissions(userId1).stream() + .collect(Collectors.toSet()); + + Set<PermissionEntity> permissionsToAdd + = permissionsDAO.findUserPermissions(userId2).stream() + .map(p -> { + p.setUserId(userId1); + return p; + }) + .filter(p -> isPermissionToAdd(existingPermissions, p)) + .collect(Collectors.toSet()); + + joinDAO.join(membershipsToAdd, permissionsToAdd, userId2); + } + + private MembershipEntity getMembershipEntity(String groupId, String userId) { + MembershipEntity entity = new MembershipEntity(); + entity.setGroupId(groupId); + entity.setUserId(userId); + return entity; + } + + private boolean isPermissionToAdd(Set<PermissionEntity> existingPermissions, PermissionEntity permissionToCheck) { + for (PermissionEntity permission : existingPermissions) { + if (permission.getGroupId().equals(permissionToCheck.getGroupId()) + && permission.getUserId().equals(permissionToCheck.getUserId())) { + if (permission.getPermission() == permissionToCheck.getPermission()) { + return false; + } + Permission strongerPermission = Permission.addPermission( + permission.getPermission(), permissionToCheck.getPermission()); + return permission.getPermission() != strongerPermission; + } + } + return true; + } +} diff --git a/gms/src/main/java/it/inaf/ia2/gms/service/MembersService.java b/gms/src/main/java/it/inaf/ia2/gms/service/MembersService.java index b05fb529c129b34961dac11bdc4b6a32d0d29b40..4adfa775e74464b3eb755267b473d9ecb96da33e 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/service/MembersService.java +++ b/gms/src/main/java/it/inaf/ia2/gms/service/MembersService.java @@ -42,8 +42,4 @@ public class MembersService { public void removeMember(String groupId, String userId) { membershipsDAO.removeMembership(groupId, userId); } - - public void moveMemberships(String fromUserId, String toUserId) { - membershipsDAO.moveMemberships(fromUserId, toUserId); - } } diff --git a/gms/src/main/java/it/inaf/ia2/gms/service/PermissionsService.java b/gms/src/main/java/it/inaf/ia2/gms/service/PermissionsService.java index 80ef491cbbc8ed45b5bca009100795c0c083a043..99128493e8680fd60154b889e884b0e0d34c4ae5 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/service/PermissionsService.java +++ b/gms/src/main/java/it/inaf/ia2/gms/service/PermissionsService.java @@ -87,8 +87,4 @@ public class PermissionsService { return permissionsDAO.createOrUpdatePermission(permissionEntity); } - - public void movePermissions(String fromUserId, String toUserId) { - permissionsDAO.movePermissions(fromUserId, toUserId); - } } diff --git a/gms/src/main/resources/application.properties b/gms/src/main/resources/application.properties index a4dec9aa458982bcbf9c04922ca172023598d1d7..eaa17ffd730877b8348294406708298e7768f693 100644 --- a/gms/src/main/resources/application.properties +++ b/gms/src/main/resources/application.properties @@ -1,4 +1,4 @@ -server.port=8081 +server.port=8082 server.servlet.context-path=/gms security.oauth2.client.client-id=gms @@ -9,6 +9,7 @@ security.oauth2.resource.token-info-uri=http://localhost/rap-ia2/auth/oauth2/che security.oauth2.client.scope=openid,email,profile security.oauth2.resource.jwk.key-set-uri=http://localhost/rap-ia2/auth/oidc/jwks +logging.level.it.inaf=TRACE logging.level.org.springframework.security=DEBUG logging.level.org.springframework.jdbc=TRACE logging.level.org.springframework.web=TRACE diff --git a/gms/src/test/java/it/inaf/ia2/gms/controller/BasicAuthWebServiceControllerTest.java b/gms/src/test/java/it/inaf/ia2/gms/controller/BasicAuthWebServiceControllerTest.java index 0061ca3d6d6656295beba48ba5cc9ccb7965d3f2..8594bdb8f82f02ab3822f95fcfae75ee21d2c83d 100644 --- a/gms/src/test/java/it/inaf/ia2/gms/controller/BasicAuthWebServiceControllerTest.java +++ b/gms/src/test/java/it/inaf/ia2/gms/controller/BasicAuthWebServiceControllerTest.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import it.inaf.ia2.gms.model.request.AddMemberWsRequest; import it.inaf.ia2.gms.model.request.AddPermissionWsRequest; import it.inaf.ia2.gms.model.Permission; -import it.inaf.ia2.gms.model.PrepareToJoinRequest; import it.inaf.ia2.gms.persistence.model.GroupEntity; import it.inaf.ia2.gms.persistence.model.MembershipEntity; import it.inaf.ia2.gms.persistence.model.PermissionEntity; @@ -194,22 +193,6 @@ public class BasicAuthWebServiceControllerTest { verify(permissionsService, times(1)).removePermission(eq(inaf), eq("user_id")); } - @Test - public void testPrepareToJoin() throws Exception { - - PrepareToJoinRequest request = new PrepareToJoinRequest(); - request.setFromUserId("from_user"); - request.setToUserId("to_user"); - - mockMvc.perform(post("/ws/basic/prepare-join") - .content(mapper.writeValueAsString(request)) - .contentType(MediaType.APPLICATION_JSON_UTF8)) - .andExpect(status().isOk()); - - verify(permissionsService, times(1)).movePermissions(request.getFromUserId(), request.getToUserId()); - verify(membersService, times(1)).moveMemberships(request.getFromUserId(), request.getToUserId()); - } - private GroupEntity getInafGroup() { GroupEntity inaf = new GroupEntity(); inaf.setId("inaf_id"); diff --git a/gms/src/test/java/it/inaf/ia2/gms/persistence/GroupsDAOTest.java b/gms/src/test/java/it/inaf/ia2/gms/persistence/GroupsDAOTest.java index ff5d365e12a385d17e0f3ef6f800f95e77c04e91..271779ebbda4867291013750c1e22743ef377189 100644 --- a/gms/src/test/java/it/inaf/ia2/gms/persistence/GroupsDAOTest.java +++ b/gms/src/test/java/it/inaf/ia2/gms/persistence/GroupsDAOTest.java @@ -34,7 +34,6 @@ public class GroupsDAOTest { } @Test - //@Sql("/sql/init.sql") public void testAll() { // Create groups diff --git a/gms/src/test/java/it/inaf/ia2/gms/persistence/JoinDAOTest.java b/gms/src/test/java/it/inaf/ia2/gms/persistence/JoinDAOTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c1ca308d24ca9d7251cd6f559a5ba23eeae0c1d2 --- /dev/null +++ b/gms/src/test/java/it/inaf/ia2/gms/persistence/JoinDAOTest.java @@ -0,0 +1,99 @@ +package it.inaf.ia2.gms.persistence; + +import it.inaf.ia2.gms.DataSourceConfig; +import it.inaf.ia2.gms.model.Permission; +import it.inaf.ia2.gms.persistence.model.GroupEntity; +import it.inaf.ia2.gms.persistence.model.MembershipEntity; +import it.inaf.ia2.gms.persistence.model.PermissionEntity; +import java.util.HashSet; +import java.util.Set; +import javax.sql.DataSource; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@ContextConfiguration(classes = DataSourceConfig.class) +public class JoinDAOTest { + + private static final String USER_1 = "user-1"; + private static final String USER_2 = "user-2"; + + @Autowired + private DataSource dataSource; + + private GroupsDAO groupsDAO; + private MembershipsDAO membershipsDAO; + private PermissionsDAO permissionsDAO; + private JoinDAO joinDAO; + + @Before + public void setUp() { + groupsDAO = new GroupsDAO(dataSource); + membershipsDAO = new MembershipsDAO(dataSource); + permissionsDAO = new PermissionsDAO(dataSource); + joinDAO = new JoinDAO(dataSource); + } + + @Test + public void testJoin() { + + groupsDAO.createGroup(groupEntity("A")); + groupsDAO.createGroup(groupEntity("B")); + + permissionsDAO.createOrUpdatePermission(permissionEntity(USER_1, "A", Permission.VIEW_MEMBERS)); + permissionsDAO.createOrUpdatePermission(permissionEntity(USER_1, "B", Permission.VIEW_MEMBERS)); + permissionsDAO.createOrUpdatePermission(permissionEntity(USER_2, "A", Permission.ADMIN)); + + membershipsDAO.addMember(membershipEntity(USER_2, "A")); + + Set<MembershipEntity> membershipsToAdd = new HashSet<>(); + membershipsToAdd.add(membershipEntity(USER_1, "A")); + + Set<PermissionEntity> permissionsToAdd = new HashSet<>(); + permissionsToAdd.add(permissionEntity(USER_1, "A", Permission.ADMIN)); + + assertEquals(1, membershipsDAO.getUserMemberships(USER_2).size()); + assertEquals(2, permissionsDAO.findUserPermissions(USER_1).size()); + assertEquals(1, permissionsDAO.findUserPermissions(USER_2).size()); + + joinDAO.join(membershipsToAdd, permissionsToAdd, USER_2); + + assertTrue(membershipsDAO.getUserMemberships(USER_2).isEmpty()); + assertTrue(permissionsDAO.findUserPermissions(USER_2).isEmpty()); + assertEquals(2, permissionsDAO.findUserPermissions(USER_1).size()); + assertTrue(permissionsDAO.findUserPermissions(USER_1) + .contains(permissionEntity(USER_1, "A", Permission.ADMIN))); + assertEquals(1, membershipsDAO.getUserMemberships(USER_1).size()); + } + + private GroupEntity groupEntity(String groupId) { + GroupEntity groupEntity = new GroupEntity(); + groupEntity.setId(groupId); + groupEntity.setName(groupId); + groupEntity.setPath(groupId); + groupEntity.setLeaf(false); + return groupEntity; + } + + private MembershipEntity membershipEntity(String userId, String groupId) { + MembershipEntity membershipEntity = new MembershipEntity(); + membershipEntity.setUserId(userId); + membershipEntity.setGroupId(groupId); + return membershipEntity; + } + + private PermissionEntity permissionEntity(String userId, String groupId, Permission permission) { + PermissionEntity permissionEntity = new PermissionEntity(); + permissionEntity.setGroupId(groupId); + permissionEntity.setUserId(userId); + permissionEntity.setPermission(permission); + permissionEntity.setGroupPath(groupId); + return permissionEntity; + } +} diff --git a/gms/src/test/java/it/inaf/ia2/gms/service/JoinServiceTest.java b/gms/src/test/java/it/inaf/ia2/gms/service/JoinServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..87d0402c99aae050163ac0f16f2716775a7ce7e2 --- /dev/null +++ b/gms/src/test/java/it/inaf/ia2/gms/service/JoinServiceTest.java @@ -0,0 +1,104 @@ +package it.inaf.ia2.gms.service; + +import it.inaf.ia2.gms.model.Permission; +import it.inaf.ia2.gms.persistence.JoinDAO; +import it.inaf.ia2.gms.persistence.MembershipsDAO; +import it.inaf.ia2.gms.persistence.PermissionsDAO; +import it.inaf.ia2.gms.persistence.model.GroupEntity; +import it.inaf.ia2.gms.persistence.model.MembershipEntity; +import it.inaf.ia2.gms.persistence.model.PermissionEntity; +import java.util.Arrays; +import java.util.Set; +import static org.junit.Assert.assertEquals; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentMatcher; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class JoinServiceTest { + + private static final String USER_1 = "user-1"; + private static final String USER_2 = "user-2"; + + @Mock + private MembershipsDAO membershipsDAO; + + @Mock + private PermissionsDAO permissionsDAO; + + @Mock + private JoinDAO joinDAO; + + @InjectMocks + private JoinService joinService; + + @Test + public void testJoin() { + + when(membershipsDAO.getUserMemberships(eq(USER_1))) + .thenReturn(Arrays.asList(groupEntity("A"))); + + when(membershipsDAO.getUserMemberships(eq(USER_2))) + .thenReturn(Arrays.asList(groupEntity("A"), groupEntity("B"))); + + when(permissionsDAO.findUserPermissions(eq(USER_1))) + .thenReturn(Arrays.asList( + permissionEntity("A", USER_1, Permission.ADMIN), + permissionEntity("B", USER_1, Permission.VIEW_MEMBERS), + permissionEntity("C", USER_1, Permission.MANAGE_MEMBERS) + )); + + when(permissionsDAO.findUserPermissions(eq(USER_2))) + .thenReturn(Arrays.asList( + permissionEntity("A", USER_2, Permission.VIEW_MEMBERS), + permissionEntity("B", USER_2, Permission.ADMIN), + permissionEntity("C", USER_2, Permission.MANAGE_MEMBERS), + permissionEntity("D", USER_2, Permission.VIEW_MEMBERS) + )); + + joinService.join(USER_1, USER_2); + + verify(joinDAO, times(1)).join(argThat(verifyMembershipsToAdd()), + argThat(verifyPermissionsToAdd()), eq(USER_2)); + } + + private GroupEntity groupEntity(String groupId) { + GroupEntity group = new GroupEntity(); + group.setId(groupId); + return group; + } + + private PermissionEntity permissionEntity(String groupId, String userId, Permission permission) { + PermissionEntity permissionEntity = new PermissionEntity(); + permissionEntity.setGroupId(groupId); + permissionEntity.setUserId(userId); + permissionEntity.setPermission(permission); + return permissionEntity; + } + + private ArgumentMatcher<Set<MembershipEntity>> verifyMembershipsToAdd() { + return memberships -> { + assertEquals(1, memberships.size()); + MembershipEntity entity = memberships.iterator().next(); + assertEquals("B", entity.getGroupId()); + assertEquals(USER_1, entity.getUserId()); + return true; + }; + } + + private ArgumentMatcher<Set<PermissionEntity>> verifyPermissionsToAdd() { + return permissions -> { + assertEquals(2, permissions.size()); + return permissions.contains(permissionEntity("B", USER_1, Permission.ADMIN)) + && permissions.contains(permissionEntity("D", USER_1, Permission.VIEW_MEMBERS)); + }; + } +}