Skip to content
Snippets Groups Projects
Commit d6ee290f authored by Sonia Zorba's avatar Sonia Zorba
Browse files

Join implementation changes

parent 9cbdd322
No related branches found
No related tags found
No related merge requests found
Showing
with 310 additions and 137 deletions
......@@ -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);
}
......
......@@ -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());
}
......
......@@ -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);
......
......@@ -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) {
......
......@@ -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;
}
}
}
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;
}
}
......@@ -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"));
......
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);
}
}
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;
}
}
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;
});
}
}
......@@ -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 ("
......
......@@ -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 ("
......
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);
}
}
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;
}
}
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;
}
}
......@@ -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);
}
}
......@@ -87,8 +87,4 @@ public class PermissionsService {
return permissionsDAO.createOrUpdatePermission(permissionEntity);
}
public void movePermissions(String fromUserId, String toUserId) {
permissionsDAO.movePermissions(fromUserId, toUserId);
}
}
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
......
......@@ -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");
......
......@@ -34,7 +34,6 @@ public class GroupsDAOTest {
}
@Test
//@Sql("/sql/init.sql")
public void testAll() {
// Create groups
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment