diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java index ca8f3eba29..c3663b82b5 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java @@ -11,6 +11,7 @@ import sonia.scm.plugin.PluginState; import sonia.scm.web.VndMediaType; import javax.inject.Inject; +import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; @@ -66,6 +67,7 @@ public class AvailablePluginResource { @Path("/{name}/{version}") @StatusCodes({ @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 404, condition = "not found"), @ResponseCode(code = 500, condition = "internal server error") }) @TypeHint(PluginDto.class) @@ -91,6 +93,7 @@ public class AvailablePluginResource { */ @POST @Path("/{name}/{version}/install") + @Consumes(VndMediaType.PLUGIN) @StatusCodes({ @ResponseCode(code = 200, condition = "success"), @ResponseCode(code = 500, condition = "internal server error") diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java new file mode 100644 index 0000000000..1ec1c52e81 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java @@ -0,0 +1,169 @@ +package sonia.scm.api.v2.resources; + +import de.otto.edison.hal.HalRepresentation; +import org.apache.shiro.subject.Subject; +import org.apache.shiro.util.ThreadContext; +import org.jboss.resteasy.core.Dispatcher; +import org.jboss.resteasy.mock.MockDispatcherFactory; +import org.jboss.resteasy.mock.MockHttpRequest; +import org.jboss.resteasy.mock.MockHttpResponse; +import org.jboss.resteasy.spi.UnhandledException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import sonia.scm.plugin.PluginInformation; +import sonia.scm.plugin.PluginManager; +import sonia.scm.plugin.PluginState; +import sonia.scm.web.VndMediaType; + +import javax.inject.Provider; +import javax.servlet.http.HttpServletResponse; + +import java.io.UnsupportedEncodingException; +import java.net.URISyntaxException; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class AvailablePluginResourceTest { + + private Dispatcher dispatcher; + + @Mock + Provider installedPluginResourceProvider; + + @Mock + Provider availablePluginResourceProvider; + + @Mock + private PluginDtoCollectionMapper collectionMapper; + + @Mock + private PluginManager pluginManager; + + @InjectMocks + AvailablePluginResource availablePluginResource; + + PluginRootResource pluginRootResource; + + private final Subject subject = mock(Subject.class); + + + @BeforeEach + void prepareEnvironment() { + dispatcher = MockDispatcherFactory.createDispatcher(); + pluginRootResource = new PluginRootResource(installedPluginResourceProvider, availablePluginResourceProvider); + when(availablePluginResourceProvider.get()).thenReturn(availablePluginResource); + dispatcher.getRegistry().addSingletonResource(pluginRootResource); + } + + @Nested + class withAuthorization { + + @BeforeEach + void bindSubject() { + ThreadContext.bind(subject); + when(subject.isPermitted(any(String.class))).thenReturn(true); + } + + @AfterEach + public void unbindSubject() { + ThreadContext.unbindSubject(); + } + + @Test + void getAvailablePlugins() throws URISyntaxException, UnsupportedEncodingException { + PluginInformation pluginInformation = new PluginInformation(); + pluginInformation.setState(PluginState.AVAILABLE); + when(pluginManager.getAvailable()).thenReturn(Collections.singletonList(pluginInformation)); + when(collectionMapper.map(Collections.singletonList(pluginInformation))).thenReturn(new MockedResultDto()); + + MockHttpRequest request = MockHttpRequest.get("/v2/plugins/available"); + request.accept(VndMediaType.PLUGIN_COLLECTION); + MockHttpResponse response = new MockHttpResponse(); + + dispatcher.invoke(request, response); + + assertThat(HttpServletResponse.SC_OK).isEqualTo(response.getStatus()); + assertThat(response.getContentAsString()).contains("\"marker\":\"x\""); + } + + @Test + void getAvailablePlugin() throws UnsupportedEncodingException, URISyntaxException { + PluginInformation pluginInformation = new PluginInformation(); + pluginInformation.setState(PluginState.AVAILABLE); + pluginInformation.setName("pluginName"); + pluginInformation.setVersion("2.0.0"); + when(pluginManager.getAvailable()).thenReturn(Collections.singletonList(pluginInformation)); + + MockHttpRequest request = MockHttpRequest.get("/v2/plugins/available/pluginName/2.0.0"); + request.accept(VndMediaType.PLUGIN); + MockHttpResponse response = new MockHttpResponse(); + + dispatcher.invoke(request, response); + + assertThat(HttpServletResponse.SC_OK).isEqualTo(response.getStatus()); + assertThat(response.getContentAsString()).contains("\"name\":\"pluginName\""); + } + + @Test + void installPlugin() throws URISyntaxException { + MockHttpRequest request = MockHttpRequest.post("/v2/plugins/available/pluginName/2.0.0/install"); + request.accept(VndMediaType.PLUGIN); + MockHttpResponse response = new MockHttpResponse(); + + dispatcher.invoke(request, response); + + verify(pluginManager).install("pluginName:2.0.0"); + assertThat(HttpServletResponse.SC_OK).isEqualTo(response.getStatus()); + } + } + + @Nested + class WithoutAuthorization { + + @Test + void shouldNotGetAvailablePluginsIfMissingPermission() throws URISyntaxException { + MockHttpRequest request = MockHttpRequest.get("/v2/plugins/available"); + request.accept(VndMediaType.PLUGIN_COLLECTION); + MockHttpResponse response = new MockHttpResponse(); + + assertThrows(UnhandledException.class, () -> dispatcher.invoke(request, response)); + } + + @Test + void shouldNotGetAvailablePluginIfMissingPermission() throws URISyntaxException { + MockHttpRequest request = MockHttpRequest.get("/v2/plugins/available/pluginName/2.0.0"); + request.accept(VndMediaType.PLUGIN); + MockHttpResponse response = new MockHttpResponse(); + + assertThrows(UnhandledException.class, () -> dispatcher.invoke(request, response)); + } + + @Test + void shouldNotInstallPluginIfMissingPermission() throws URISyntaxException { + MockHttpRequest request = MockHttpRequest.post("/v2/plugins/available/pluginName/2.0.0/install"); + request.accept(VndMediaType.PLUGIN); + MockHttpResponse response = new MockHttpResponse(); + + assertThrows(UnhandledException.class, () -> dispatcher.invoke(request, response)); + } + } + + public class MockedResultDto extends HalRepresentation { + public String getMarker() { + return "x"; + } + } +} diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java index acca905320..f6633b4965 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java @@ -1,6 +1,5 @@ package sonia.scm.api.v2.resources; -import com.google.common.io.Resources; import de.otto.edison.hal.HalRepresentation; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.ThreadContext; @@ -8,6 +7,7 @@ import org.jboss.resteasy.core.Dispatcher; import org.jboss.resteasy.mock.MockDispatcherFactory; import org.jboss.resteasy.mock.MockHttpRequest; import org.jboss.resteasy.mock.MockHttpResponse; +import org.jboss.resteasy.spi.UnhandledException; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; @@ -19,7 +19,6 @@ import org.mockito.junit.jupiter.MockitoExtension; import sonia.scm.plugin.Plugin; import sonia.scm.plugin.PluginInformation; import sonia.scm.plugin.PluginLoader; -import sonia.scm.plugin.PluginManager; import sonia.scm.plugin.PluginState; import sonia.scm.plugin.PluginWrapper; import sonia.scm.web.VndMediaType; @@ -28,10 +27,10 @@ import javax.inject.Provider; import javax.servlet.http.HttpServletResponse; import java.io.UnsupportedEncodingException; import java.net.URISyntaxException; -import java.net.URL; import java.util.Collections; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -40,7 +39,6 @@ import static org.mockito.Mockito.when; class InstalledPluginResourceTest { private Dispatcher dispatcher; - private URL resources = Resources.getResource("sonia/scm/api/v2/installedPlugins-001.json"); @Mock Provider installedPluginResourceProvider; @@ -48,9 +46,6 @@ class InstalledPluginResourceTest { @Mock Provider availablePluginResourceProvider; - @Mock - private PluginManager pluginManager; - @Mock private PluginLoader pluginLoader; @@ -91,17 +86,8 @@ class InstalledPluginResourceTest { @Test void getInstalledPlugins() throws URISyntaxException, UnsupportedEncodingException { - PluginInformation pluginInformation = new PluginInformation(); - pluginInformation.setVersion("2.0.0"); - pluginInformation.setName("plugin-name"); - pluginInformation.setState(PluginState.INSTALLED); - Plugin plugin = new Plugin(2, pluginInformation, null, null, false, null); - PluginWrapper pluginWrapper = new PluginWrapper(plugin, null, null, null); + PluginWrapper pluginWrapper = new PluginWrapper(null, null, null, null); when(pluginLoader.getInstalledPlugins()).thenReturn(Collections.singletonList(pluginWrapper)); - - PluginDto pluginDto = new PluginDto(); - pluginDto.setName("plugin-name"); - pluginDto.setVersion("2.0.0"); when(collectionMapper.map(Collections.singletonList(pluginWrapper))).thenReturn(new MockedResultDto()); MockHttpRequest request = MockHttpRequest.get("/v2/plugins/installed"); @@ -115,13 +101,55 @@ class InstalledPluginResourceTest { } @Test - void getInstalledPlugin() { + void getInstalledPlugin() throws UnsupportedEncodingException, URISyntaxException { + PluginInformation pluginInformation = new PluginInformation(); + pluginInformation.setVersion("2.0.0"); + pluginInformation.setName("pluginName"); + pluginInformation.setState(PluginState.INSTALLED); + Plugin plugin = new Plugin(2, pluginInformation, null, null, false, null); + PluginWrapper pluginWrapper = new PluginWrapper(plugin, null, null, null); + when(pluginLoader.getInstalledPlugins()).thenReturn(Collections.singletonList(pluginWrapper)); + + PluginDto pluginDto = new PluginDto(); + pluginDto.setName("pluginName"); + when(mapper.map(pluginWrapper)).thenReturn(pluginDto); + + MockHttpRequest request = MockHttpRequest.get("/v2/plugins/installed/pluginName"); + request.accept(VndMediaType.PLUGIN); + MockHttpResponse response = new MockHttpResponse(); + + dispatcher.invoke(request, response); + + assertThat(HttpServletResponse.SC_OK).isEqualTo(response.getStatus()); + assertThat(response.getContentAsString()).contains("\"name\":\"pluginName\""); + } + } + + @Nested + class WithoutAuthorization { + + @Test + void shouldNotGetInstalledPluginsIfMissingPermission() throws URISyntaxException { + MockHttpRequest request = MockHttpRequest.get("/v2/plugins/installed"); + request.accept(VndMediaType.PLUGIN_COLLECTION); + MockHttpResponse response = new MockHttpResponse(); + + assertThrows(UnhandledException.class, () -> dispatcher.invoke(request, response)); } - public class MockedResultDto extends HalRepresentation { - public String getMarker() { - return "x"; - } + @Test + void shouldNotGetInstalledPluginIfMissingPermission() throws URISyntaxException { + MockHttpRequest request = MockHttpRequest.get("/v2/plugins/installed/pluginName"); +// request.accept(VndMediaType.PLUGIN); + MockHttpResponse response = new MockHttpResponse(); + + assertThrows(UnhandledException.class, () -> dispatcher.invoke(request, response)); + } + } + + public class MockedResultDto extends HalRepresentation { + public String getMarker() { + return "x"; } } }