Loading gms/src/main/java/it/inaf/ia2/gms/controller/JWTWebServiceController.java +58 −0 Original line number Original line Diff line number Diff line Loading @@ -14,12 +14,14 @@ import java.util.HashMap; import java.util.HashSet; import java.util.HashSet; import java.util.List; import java.util.List; import java.util.Map; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.Set; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController; Loading Loading @@ -64,6 +66,62 @@ public class JWTWebServiceController { } } } } /** * This endpoint is compliant with the IVOA GMS standard. Warning: for * supporting the groups of groups (with dots inside) the path variable must * be defined adding ".+", otherwise Spring will think it is a file * extension (thanks https://stackoverflow.com/a/16333149/771431) */ @GetMapping(value = "/search/{group:.+}", produces = MediaType.TEXT_PLAIN_VALUE) public void isMemberOf(@PathVariable("group") String group, Principal principal, HttpServletResponse response) throws IOException { String userId = principal.getName(); List<String> groupNames = extractGroupNames(group); boolean isMemeber = false; String parentPath = ""; // starting from ROOT for (String groupName : groupNames) { Optional<GroupEntity> optionalGroup = groupsDAO.findGroupByParentAndName(parentPath, groupName); if (optionalGroup.isPresent()) { GroupEntity groupEntity = optionalGroup.get(); parentPath = groupEntity.getPath(); isMemeber = membershipsDAO.isMemberOf(userId, groupEntity.getId()); if (isMemeber) { break; } } else { break; } } if (isMemeber) { try (PrintWriter pw = new PrintWriter(response.getOutputStream())) { pw.println(group); } } // else: empty response (as defined by GMS standard) } private List<String> extractGroupNames(String groupStr) { List<String> names = new ArrayList<>(); String currentName = ""; for (int i = 0; i < groupStr.length(); i++) { char c = groupStr.charAt(i); // dot is the group separator and it must be escaped if used inside // group names if (c == '.' && groupStr.charAt(i - 1) != '\\') { names.add(currentName.replace("\\.", ".")); currentName = ""; } else { currentName += c; } } names.add(currentName); return names; } private Set<String> getAllIdentifiers(List<GroupEntity> groups) { private Set<String> getAllIdentifiers(List<GroupEntity> groups) { Set<String> allIdentifiers = new HashSet<>(); Set<String> allIdentifiers = new HashSet<>(); Loading gms/src/main/java/it/inaf/ia2/gms/persistence/LoggingDAO.java +13 −9 Original line number Original line Diff line number Diff line Loading @@ -53,6 +53,7 @@ public class LoggingDAO { } } public void logAction(String action) { public void logAction(String action) { try { String sql = "INSERT INTO audit_log (\"user\", action, ip_address) VALUES (?, ?, ?)"; String sql = "INSERT INTO audit_log (\"user\", action, ip_address) VALUES (?, ?, ?)"; jdbcTemplate.update(conn -> { jdbcTemplate.update(conn -> { Loading @@ -63,6 +64,9 @@ public class LoggingDAO { ps.setString(++i, getIPAddress()); ps.setString(++i, getIPAddress()); return ps; return ps; }); }); } catch (Throwable t) { LOG.error("Exception while trying to save audit log", t); } } } private String getIPAddress() { private String getIPAddress() { Loading gms/src/test/java/it/inaf/ia2/gms/controller/JWTWebServiceControllerTest.java 0 → 100644 +82 −0 Original line number Original line Diff line number Diff line package it.inaf.ia2.gms.controller; import it.inaf.ia2.gms.persistence.GroupsDAO; import it.inaf.ia2.gms.persistence.MembershipsDAO; import it.inaf.ia2.gms.persistence.model.GroupEntity; import java.security.Principal; import java.util.Optional; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import org.mockito.InjectMocks; import org.mockito.Mock; import static org.mockito.Mockito.when; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.web.servlet.MockMvc; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @RunWith(MockitoJUnitRunner.class) public class JWTWebServiceControllerTest { @Mock private MembershipsDAO membershipsDAO; @Mock private GroupsDAO groupsDAO; @InjectMocks private JWTWebServiceController controller; private final Principal principal = new Principal() { @Override public String getName() { return "TEST_PRINCIPAL"; } }; private MockMvc mockMvc; @Before public void init() { mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); } @Test public void testIsMemberOf() throws Exception { GroupEntity group1 = new GroupEntity(); group1.setId("1"); group1.setPath("1"); group1.setName("group.1"); GroupEntity group2 = new GroupEntity(); group2.setId("2"); group2.setPath("1.2"); group2.setName("subgroup"); GroupEntity group3 = new GroupEntity(); group3.setId("3"); group3.setPath("1.2.3"); group3.setName("subsubgroup"); when(groupsDAO.findGroupByParentAndName(eq(""), eq("group.1"))) .thenReturn(Optional.of(group1)); when(groupsDAO.findGroupByParentAndName(eq("1"), eq("subgroup"))) .thenReturn(Optional.of(group2)); when(groupsDAO.findGroupByParentAndName(eq("1.2"), eq("subsubgroup"))) .thenReturn(Optional.of(group3)); when(membershipsDAO.isMemberOf(anyString(), eq(group3.getId()))).thenReturn(true); String group = "group\\.1.subgroup.subsubgroup"; mockMvc.perform(get("/ws/jwt/search/" + group).principal(principal)) .andExpect(status().isOk()) .andExpect(content().string(group + "\n")); } } Loading
gms/src/main/java/it/inaf/ia2/gms/controller/JWTWebServiceController.java +58 −0 Original line number Original line Diff line number Diff line Loading @@ -14,12 +14,14 @@ import java.util.HashMap; import java.util.HashSet; import java.util.HashSet; import java.util.List; import java.util.List; import java.util.Map; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.Set; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController; Loading Loading @@ -64,6 +66,62 @@ public class JWTWebServiceController { } } } } /** * This endpoint is compliant with the IVOA GMS standard. Warning: for * supporting the groups of groups (with dots inside) the path variable must * be defined adding ".+", otherwise Spring will think it is a file * extension (thanks https://stackoverflow.com/a/16333149/771431) */ @GetMapping(value = "/search/{group:.+}", produces = MediaType.TEXT_PLAIN_VALUE) public void isMemberOf(@PathVariable("group") String group, Principal principal, HttpServletResponse response) throws IOException { String userId = principal.getName(); List<String> groupNames = extractGroupNames(group); boolean isMemeber = false; String parentPath = ""; // starting from ROOT for (String groupName : groupNames) { Optional<GroupEntity> optionalGroup = groupsDAO.findGroupByParentAndName(parentPath, groupName); if (optionalGroup.isPresent()) { GroupEntity groupEntity = optionalGroup.get(); parentPath = groupEntity.getPath(); isMemeber = membershipsDAO.isMemberOf(userId, groupEntity.getId()); if (isMemeber) { break; } } else { break; } } if (isMemeber) { try (PrintWriter pw = new PrintWriter(response.getOutputStream())) { pw.println(group); } } // else: empty response (as defined by GMS standard) } private List<String> extractGroupNames(String groupStr) { List<String> names = new ArrayList<>(); String currentName = ""; for (int i = 0; i < groupStr.length(); i++) { char c = groupStr.charAt(i); // dot is the group separator and it must be escaped if used inside // group names if (c == '.' && groupStr.charAt(i - 1) != '\\') { names.add(currentName.replace("\\.", ".")); currentName = ""; } else { currentName += c; } } names.add(currentName); return names; } private Set<String> getAllIdentifiers(List<GroupEntity> groups) { private Set<String> getAllIdentifiers(List<GroupEntity> groups) { Set<String> allIdentifiers = new HashSet<>(); Set<String> allIdentifiers = new HashSet<>(); Loading
gms/src/main/java/it/inaf/ia2/gms/persistence/LoggingDAO.java +13 −9 Original line number Original line Diff line number Diff line Loading @@ -53,6 +53,7 @@ public class LoggingDAO { } } public void logAction(String action) { public void logAction(String action) { try { String sql = "INSERT INTO audit_log (\"user\", action, ip_address) VALUES (?, ?, ?)"; String sql = "INSERT INTO audit_log (\"user\", action, ip_address) VALUES (?, ?, ?)"; jdbcTemplate.update(conn -> { jdbcTemplate.update(conn -> { Loading @@ -63,6 +64,9 @@ public class LoggingDAO { ps.setString(++i, getIPAddress()); ps.setString(++i, getIPAddress()); return ps; return ps; }); }); } catch (Throwable t) { LOG.error("Exception while trying to save audit log", t); } } } private String getIPAddress() { private String getIPAddress() { Loading
gms/src/test/java/it/inaf/ia2/gms/controller/JWTWebServiceControllerTest.java 0 → 100644 +82 −0 Original line number Original line Diff line number Diff line package it.inaf.ia2.gms.controller; import it.inaf.ia2.gms.persistence.GroupsDAO; import it.inaf.ia2.gms.persistence.MembershipsDAO; import it.inaf.ia2.gms.persistence.model.GroupEntity; import java.security.Principal; import java.util.Optional; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import org.mockito.InjectMocks; import org.mockito.Mock; import static org.mockito.Mockito.when; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.web.servlet.MockMvc; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @RunWith(MockitoJUnitRunner.class) public class JWTWebServiceControllerTest { @Mock private MembershipsDAO membershipsDAO; @Mock private GroupsDAO groupsDAO; @InjectMocks private JWTWebServiceController controller; private final Principal principal = new Principal() { @Override public String getName() { return "TEST_PRINCIPAL"; } }; private MockMvc mockMvc; @Before public void init() { mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); } @Test public void testIsMemberOf() throws Exception { GroupEntity group1 = new GroupEntity(); group1.setId("1"); group1.setPath("1"); group1.setName("group.1"); GroupEntity group2 = new GroupEntity(); group2.setId("2"); group2.setPath("1.2"); group2.setName("subgroup"); GroupEntity group3 = new GroupEntity(); group3.setId("3"); group3.setPath("1.2.3"); group3.setName("subsubgroup"); when(groupsDAO.findGroupByParentAndName(eq(""), eq("group.1"))) .thenReturn(Optional.of(group1)); when(groupsDAO.findGroupByParentAndName(eq("1"), eq("subgroup"))) .thenReturn(Optional.of(group2)); when(groupsDAO.findGroupByParentAndName(eq("1.2"), eq("subsubgroup"))) .thenReturn(Optional.of(group3)); when(membershipsDAO.isMemberOf(anyString(), eq(group3.getId()))).thenReturn(true); String group = "group\\.1.subgroup.subsubgroup"; mockMvc.perform(get("/ws/jwt/search/" + group).principal(principal)) .andExpect(status().isOk()) .andExpect(content().string(group + "\n")); } }