From 59ad16913e913b0957350a1df88212b035436aab Mon Sep 17 00:00:00 2001 From: Sonia Zorba <sonia.zorba@inaf.it> Date: Fri, 12 Nov 2021 17:07:21 +0100 Subject: [PATCH] Added availability and capabilities controllers --- .../it/inaf/ia2/gms/authn/GmsLoginFilter.java | 2 +- .../ia2/gms/controller/DALIController.java | 71 +++++++++++++++++ gms/src/main/resources/availability.xml | 5 ++ gms/src/main/resources/capabilities.xml | 26 +++++++ .../gms/controller/DALIControllerTest.java | 77 +++++++++++++++++++ 5 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 gms/src/main/java/it/inaf/ia2/gms/controller/DALIController.java create mode 100644 gms/src/main/resources/availability.xml create mode 100644 gms/src/main/resources/capabilities.xml create mode 100644 gms/src/test/java/it/inaf/ia2/gms/controller/DALIControllerTest.java diff --git a/gms/src/main/java/it/inaf/ia2/gms/authn/GmsLoginFilter.java b/gms/src/main/java/it/inaf/ia2/gms/authn/GmsLoginFilter.java index 575cadf..fa02aaf 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/authn/GmsLoginFilter.java +++ b/gms/src/main/java/it/inaf/ia2/gms/authn/GmsLoginFilter.java @@ -44,7 +44,7 @@ public class GmsLoginFilter extends LoginFilter { AntPathMatcher pathMatcher = new AntPathMatcher(); // Authentication is ignored for these endpoints: - return Arrays.asList("/ws/jwt/**", "/error", "/logout", "/invited-registration", "/help/**") + return Arrays.asList("/ws/jwt/**", "/error", "/logout", "/invited-registration", "/help/**", "/vo/capabilities", "/vo/availability") .stream() .anyMatch(p -> pathMatcher.match(p, request.getServletPath())); } diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/DALIController.java b/gms/src/main/java/it/inaf/ia2/gms/controller/DALIController.java new file mode 100644 index 0000000..eb5de8c --- /dev/null +++ b/gms/src/main/java/it/inaf/ia2/gms/controller/DALIController.java @@ -0,0 +1,71 @@ +/* + * This file is part of gms + * Copyright (C) 2021 Istituto Nazionale di Astrofisica + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package it.inaf.ia2.gms.controller; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Scanner; +import javax.servlet.http.HttpServletRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.util.UriComponentsBuilder; + +/** + * Controller for IVOA Data Access Layer Interface (VOSI-capabilities and + * VOSI-availability endpoints). + */ +@RestController +public class DALIController { + + @Autowired + private HttpServletRequest request; + + @GetMapping(value = "/vo/capabilities", produces = {MediaType.APPLICATION_XML_VALUE, MediaType.TEXT_XML_VALUE}) + public String getCapabilities() throws IOException { + String xml = loadXmlFile("capabilities.xml"); + return xml.replace("{{ base_url }}", getBaseUrl()); + } + + @GetMapping(value = "/vo/availability", produces = {MediaType.APPLICATION_XML_VALUE, MediaType.TEXT_XML_VALUE}) + public String getAvailability() throws IOException { + String xml = loadXmlFile("availability.xml"); + return xml.replace("{{ base_url }}", getBaseUrl()); + } + + private String loadXmlFile(String fileName) throws IOException { + try ( InputStream in = DALIController.class.getClassLoader().getResourceAsStream(fileName)) { + Scanner s = new Scanner(in).useDelimiter("\\A"); + return s.hasNext() ? s.next() : ""; + } + } + + /** + * Generate base URL considering also proxied requests. + */ + private String getBaseUrl() { + + String forwaredProtocol = request.getHeader("X-Forwarded-Proto"); + String scheme = forwaredProtocol != null ? forwaredProtocol : request.getScheme(); + + String forwardedHost = request.getHeader("X-Forwarded-Host"); + if (forwardedHost != null && forwardedHost.contains(",")) { + // X-Forwarded-Host can be a list of comma separated values + forwardedHost = forwardedHost.split(",")[0]; + } + String host = forwardedHost != null ? forwardedHost : request.getServerName(); + + UriComponentsBuilder builder = UriComponentsBuilder.newInstance() + .scheme(scheme).host(host).path(request.getContextPath()); + + if (forwardedHost == null) { + builder.port(request.getServerPort()); + } + + return builder.toUriString(); + } +} diff --git a/gms/src/main/resources/availability.xml b/gms/src/main/resources/availability.xml new file mode 100644 index 0000000..30a7f0a --- /dev/null +++ b/gms/src/main/resources/availability.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<vosi:availability xmlns:vosi="http://www.ivoa.net/xml/VOSIAvailability/v1.0"> + <vosi:available>true</vosi:available> + <vosi:note>IA2 GMS is available</vosi:note> +</vosi:availability> \ No newline at end of file diff --git a/gms/src/main/resources/capabilities.xml b/gms/src/main/resources/capabilities.xml new file mode 100644 index 0000000..7163198 --- /dev/null +++ b/gms/src/main/resources/capabilities.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<vosi:capabilities + xmlns:vosi="http://www.ivoa.net/xml/VOSICapabilities/v1.0" + xmlns:vs="http://www.ivoa.net/xml/VODataService/v1.1" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + + <capability standardID="ivo://ivoa.net/std/VOSI#capabilities"> + <interface xsi:type="vs:ParamHTTP" role="std"> + <accessURL use="full">{{ base_url }}/vo/capabilities</accessURL> + </interface> + </capability> + + <capability standardID="ivo://ivoa.net/std/VOSI#availability"> + <interface xsi:type="vs:ParamHTTP" role="std"> + <accessURL use="full">{{ base_url }}/vo/availability</accessURL> + </interface> + </capability> + + <capability standardID="ivo://ivoa.net/std/gms#search-1.0"> + <interface xsi:type="vs:ParamHTTP" role="std" version="1.0"> + <accessURL use="base">{{ base_url }}/vo/search</accessURL> + <securityMethod standardID="ivo://ivoa.net/sso#token" /> + </interface> + </capability> + +</vosi:capabilities> \ No newline at end of file diff --git a/gms/src/test/java/it/inaf/ia2/gms/controller/DALIControllerTest.java b/gms/src/test/java/it/inaf/ia2/gms/controller/DALIControllerTest.java new file mode 100644 index 0000000..05f5499 --- /dev/null +++ b/gms/src/test/java/it/inaf/ia2/gms/controller/DALIControllerTest.java @@ -0,0 +1,77 @@ +/* + * This file is part of gms + * Copyright (C) 2021 Istituto Nazionale di Astrofisica + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package it.inaf.ia2.gms.controller; + +import javax.servlet.http.HttpServletRequest; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import static org.mockito.Mockito.when; +import org.mockito.junit.jupiter.MockitoExtension; +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.status; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +@ExtendWith(MockitoExtension.class) +public class DALIControllerTest { + + private MockMvc mockMvc; + + @Mock + private HttpServletRequest request; + + @InjectMocks + private DALIController controller; + + @BeforeEach + public void init() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Test + public void testGetCapabilitiesBase() throws Exception { + + when(request.getServerName()).thenReturn("ia2.inaf.it"); + when(request.getServerPort()).thenReturn(8080); + when(request.getContextPath()).thenReturn("/gms"); + when(request.getScheme()).thenReturn("http"); + + String xml = mockMvc.perform(get("/vo/capabilities")) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); + + assertTrue(xml.contains("http://ia2.inaf.it:8080/gms/vo/search")); + } + + @Test + public void testGetCapabilitiesProxied() throws Exception { + + when(request.getHeader("X-Forwarded-Proto")).thenReturn("https"); + when(request.getHeader("X-Forwarded-Host")).thenReturn("ia2.inaf.it,server2.ia2.inaf.it"); + + when(request.getContextPath()).thenReturn("/gms"); + + String xml = mockMvc.perform(get("/vo/capabilities")) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); + + assertTrue(xml.contains("https://ia2.inaf.it/gms/vo/search")); + } + + @Test + public void testGetAvailability() throws Exception { + + String xml = mockMvc.perform(get("/vo/availability")) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); + + assertTrue(xml.contains("<vosi:available>true</vosi:available>")); + } +} -- GitLab