From f6461d4ce917af59bebade4f9fd074e29b5987c8 Mon Sep 17 00:00:00 2001 From: Dustin Jenkins Date: Thu, 17 Oct 2024 17:59:50 +0000 Subject: [PATCH] test: add tests for new post action logic --- build.gradle | 3 + .../scienceportal/session/PostAction.java | 104 +++++++++------ .../scienceportal/session/PostActionTest.java | 122 ++++++++++++++++++ 3 files changed, 189 insertions(+), 40 deletions(-) diff --git a/build.gradle b/build.gradle index 145254c..b628219 100644 --- a/build.gradle +++ b/build.gradle @@ -35,6 +35,9 @@ dependencies { runtimeOnly 'javax.servlet:jstl:1.2' runtimeOnly 'jstl:jstl:[1.0,)' runtimeOnly 'org.apache.taglibs:taglibs-standard-impl:1.2.5' + + testImplementation 'junit:junit:[4.12,5.0)' + testImplementation 'org.mockito:mockito-core:[5.14.0,6.0.0)' } sourceCompatibility = '1.8' diff --git a/src/main/java/org/opencadc/scienceportal/session/PostAction.java b/src/main/java/org/opencadc/scienceportal/session/PostAction.java index ef1cd51..22eb752 100644 --- a/src/main/java/org/opencadc/scienceportal/session/PostAction.java +++ b/src/main/java/org/opencadc/scienceportal/session/PostAction.java @@ -72,10 +72,14 @@ import ca.nrc.cadc.net.HttpPost; import ca.nrc.cadc.reg.Standards; import ca.nrc.cadc.reg.client.RegistryClient; +import ca.nrc.cadc.rest.SyncInput; import ca.nrc.cadc.util.Base64; import ca.nrc.cadc.util.StringUtil; import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; +import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.nio.charset.StandardCharsets; @@ -90,13 +94,58 @@ public class PostAction extends SciencePortalAuthAction { private static final String SESSION_ENDPOINT = "/session"; - private static final String REGISTRY_AUTH_SECRET_FROM_BROWSER = "x-registry-secret"; - private static final String REGISTRY_AUTH_USERNAME_FROM_BROWSER = "x-registry-username"; - private static final String SECRET_REQUEST_HEADER_NAME_TO_SKAHA = "x-skaha-registry-auth"; + static final String SECRET_REQUEST_HEADER_NAME_TO_SKAHA = "x-skaha-registry-auth"; + static final String REGISTRY_AUTH_SECRET_FROM_BROWSER = "x-registry-secret"; + static final String REGISTRY_AUTH_USERNAME_FROM_BROWSER = "x-registry-username"; + + PostAction(final SyncInput syncInput) { + this.syncInput = syncInput; + } @Override public void doAction() throws Exception { - final StringBuilder apiURLBuilder = new StringBuilder(getAPIURL().toExternalForm() + PostAction.SESSION_ENDPOINT); + final URL apiURL = buildAPIURL(); + final Subject authenticatedUser = getCurrentSubject(apiURL); + final HttpPost httpPost = createPostRequest(apiURL); + + try { + Subject.doAs(authenticatedUser, (PrivilegedExceptionAction) () -> { + httpPost.prepare(); + write(httpPost.getInputStream()); + + return null; + }); + } catch (PrivilegedActionException privilegedActionException) { + throw privilegedActionException.getException(); + } + } + + HttpPost createPostRequest(final URL apiURL) { + final Map payload = new HashMap<>(); + payload.putAll(syncInput.getParameterNames().stream() + .collect(Collectors.toMap(key -> key, key -> syncInput.getParameter(key) == null ? "" : syncInput.getParameter(key).trim()))); + + final HttpPost httpPost = new HttpPost(apiURL, payload, false); + + final String registrySecret = syncInput.getHeader(PostAction.REGISTRY_AUTH_SECRET_FROM_BROWSER); + final String registryUsername = syncInput.getHeader(PostAction.REGISTRY_AUTH_USERNAME_FROM_BROWSER); + + if (StringUtil.hasText(registrySecret)) { + if (StringUtil.hasText(registryUsername)) { + httpPost.setRequestProperty(PostAction.SECRET_REQUEST_HEADER_NAME_TO_SKAHA, + Base64.encodeString(registryUsername + ":" + registrySecret)); + } else { + throw new IllegalArgumentException("Secret specified but no username provided."); + } + } else if (StringUtil.hasText(registryUsername)) { + throw new IllegalArgumentException("Username specified but no secret provided."); + } + + return httpPost; + } + + URL buildAPIURL() throws MalformedURLException { + final StringBuilder apiURLBuilder = new StringBuilder(lookupAPIEndpoint().toExternalForm() + PostAction.SESSION_ENDPOINT); // Preserve path items. final String path = this.syncInput.getPath(); @@ -108,47 +157,22 @@ public void doAction() throws Exception { apiURLBuilder.append(path); } - final String registrySecret = syncInput.getHeader(PostAction.REGISTRY_AUTH_SECRET_FROM_BROWSER); - final String registryUsername = syncInput.getHeader(PostAction.REGISTRY_AUTH_USERNAME_FROM_BROWSER); - final URL apiURL = new URL(apiURLBuilder.toString()); - final Subject authenticatedUser = getCurrentSubject(apiURL); - final Map payload = new HashMap<>(); - payload.putAll(syncInput.getParameterNames().stream() - .collect(Collectors.toMap(key -> key, key -> syncInput.getParameter(key) == null ? "" : syncInput.getParameter(key).trim()))); - try { - Subject.doAs(authenticatedUser, (PrivilegedExceptionAction) () -> { - final HttpPost httpPost = new HttpPost(apiURL, payload, false); - - if (StringUtil.hasText(registrySecret)) { - if (StringUtil.hasText(registryUsername)) { - httpPost.setRequestProperty(PostAction.SECRET_REQUEST_HEADER_NAME_TO_SKAHA, - Base64.encodeString(registryUsername + ":" + registrySecret)); - } else { - throw new IllegalArgumentException("Secret specified but no username provided."); - } - } else if (StringUtil.hasText(registryUsername)) { - throw new IllegalArgumentException("Username specified but no secret provided."); - } - - httpPost.prepare(); - - final BufferedReader reader = new BufferedReader(new InputStreamReader(httpPost.getInputStream())); - String line; - while ((line = reader.readLine()) != null) { - this.syncOutput.getOutputStream().write(line.getBytes(StandardCharsets.UTF_8)); - } - this.syncOutput.getOutputStream().flush(); - return null; - }); - } catch (PrivilegedActionException privilegedActionException) { - throw privilegedActionException.getException(); - } + return new URL(apiURLBuilder.toString()); } - URL getAPIURL() { + URL lookupAPIEndpoint() { final ApplicationConfiguration applicationConfiguration = new ApplicationConfiguration(); final URI apiServiceURI = URI.create(applicationConfiguration.getResourceID()); final RegistryClient registryClient = new RegistryClient(); return registryClient.getServiceURL(apiServiceURI, Standards.PROC_SESSIONS_10, AuthMethod.TOKEN); } + + void write(final InputStream inputStream) throws IOException { + final BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + String line; + while ((line = reader.readLine()) != null) { + this.syncOutput.getOutputStream().write(line.getBytes(StandardCharsets.UTF_8)); + } + this.syncOutput.getOutputStream().flush(); + } } diff --git a/src/test/java/org/opencadc/scienceportal/session/PostActionTest.java b/src/test/java/org/opencadc/scienceportal/session/PostActionTest.java index c5e05d4..9e5d579 100644 --- a/src/test/java/org/opencadc/scienceportal/session/PostActionTest.java +++ b/src/test/java/org/opencadc/scienceportal/session/PostActionTest.java @@ -1,4 +1,126 @@ package org.opencadc.scienceportal.session; +import ca.nrc.cadc.net.HttpPost; +import ca.nrc.cadc.net.HttpRequestProperty; +import ca.nrc.cadc.rest.SyncInput; +import ca.nrc.cadc.util.Base64; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + public class PostActionTest { + @Test + public void createPostRequestMissingUsername() throws Exception { + final SyncInput mockSyncInput = Mockito.mock(SyncInput.class); + + Mockito.when(mockSyncInput.getHeader(PostAction.REGISTRY_AUTH_SECRET_FROM_BROWSER)).thenReturn("mysecret"); + Mockito.when(mockSyncInput.getHeader(PostAction.REGISTRY_AUTH_USERNAME_FROM_BROWSER)).thenReturn(null); + final Set parameterNames = new HashSet<>(); + + parameterNames.add("param1"); + + Mockito.when(mockSyncInput.getParameterNames()).thenReturn(parameterNames); + Mockito.when(mockSyncInput.getParameter("param1")).thenReturn("value1"); + + final PostAction testSubject = new PostAction(mockSyncInput); + final URL postURL = new URL("https://example.org/skaha/session-endpoint"); + + try { + testSubject.createPostRequest(postURL); + Assert.fail("Should throw an IllegalArgumentException"); + } catch (IllegalArgumentException illegalArgumentException) { + Assert.assertEquals("Wrong exception message", "Secret specified but no username provided.", illegalArgumentException.getMessage()); + } + + Mockito.verify(mockSyncInput, Mockito.times(1)).getParameterNames(); + Mockito.verify(mockSyncInput, Mockito.times(2)).getParameter("param1"); + } + + @Test + public void createPostRequestMissingSecret() throws Exception { + final SyncInput mockSyncInput = Mockito.mock(SyncInput.class); + + Mockito.when(mockSyncInput.getHeader(PostAction.REGISTRY_AUTH_SECRET_FROM_BROWSER)).thenReturn(null); + Mockito.when(mockSyncInput.getHeader(PostAction.REGISTRY_AUTH_USERNAME_FROM_BROWSER)).thenReturn("username1"); + final Set parameterNames = new HashSet<>(); + + parameterNames.add("param1"); + + Mockito.when(mockSyncInput.getParameterNames()).thenReturn(parameterNames); + Mockito.when(mockSyncInput.getParameter("param1")).thenReturn("value1"); + + final PostAction testSubject = new PostAction(mockSyncInput); + final URL postURL = new URL("https://example.org/skaha/session-endpoint"); + + try { + testSubject.createPostRequest(postURL); + Assert.fail("Should throw an IllegalArgumentException"); + } catch (IllegalArgumentException illegalArgumentException) { + Assert.assertEquals("Wrong exception message", "Username specified but no secret provided.", illegalArgumentException.getMessage()); + } + + Mockito.verify(mockSyncInput, Mockito.times(1)).getParameterNames(); + Mockito.verify(mockSyncInput, Mockito.times(2)).getParameter("param1"); + } + + @Test + public void createPostRequestPrivate() throws Exception { + final SyncInput mockSyncInput = Mockito.mock(SyncInput.class); + + Mockito.when(mockSyncInput.getHeader(PostAction.REGISTRY_AUTH_SECRET_FROM_BROWSER)).thenReturn("secret1"); + Mockito.when(mockSyncInput.getHeader(PostAction.REGISTRY_AUTH_USERNAME_FROM_BROWSER)).thenReturn("username1"); + final Set parameterNames = new HashSet<>(); + + parameterNames.add("param1"); + parameterNames.add("param2"); + + Mockito.when(mockSyncInput.getParameterNames()).thenReturn(parameterNames); + Mockito.when(mockSyncInput.getParameter("param1")).thenReturn("val ue1"); + Mockito.when(mockSyncInput.getParameter("param2")).thenReturn(" value2 "); + + final PostAction testSubject = new PostAction(mockSyncInput); + final URL postURL = new URL("https://example.org/skaha/session-endpoint"); + + final HttpPost httpPost = testSubject.createPostRequest(postURL); + + final Map postParameters = httpPost.getParameterMap(); + Assert.assertEquals("Wrong number of params.", 2, postParameters.size()); + Assert.assertTrue("Wrong param1", postParameters.containsKey("param1")); + Assert.assertEquals("Wrong value1", "val ue1", postParameters.get("param1")); + Assert.assertTrue("Wrong param2", postParameters.containsKey("param2")); + Assert.assertEquals("Wrong value2", "value2", postParameters.get("param2")); + + final List postProperties = httpPost.getRequestProperties(); + Assert.assertEquals("Wrong auth header", PostAction.SECRET_REQUEST_HEADER_NAME_TO_SKAHA, postProperties.get(0).getProperty()); + Assert.assertEquals("Wrong auth header value", Base64.encodeString("username1:secret1"), postProperties.get(0).getValue()); + + Mockito.verify(mockSyncInput, Mockito.times(1)).getParameterNames(); + Mockito.verify(mockSyncInput, Mockito.times(2)).getParameter("param1"); + Mockito.verify(mockSyncInput, Mockito.times(2)).getParameter("param2"); + } + + @Test + public void buildAPIURL() throws Exception { + final SyncInput mockSyncInput = Mockito.mock(SyncInput.class); + final URL apiEndpoint = new URL("https://example.org/skaha/session-endpoint"); + final PostAction testSubject = new PostAction(mockSyncInput) { + @Override + URL lookupAPIEndpoint() { + return apiEndpoint; + } + }; + + Mockito.when(mockSyncInput.getPath()).thenReturn("/version1/endpoint"); + + final URL apiURL = testSubject.buildAPIURL(); + Assert.assertEquals("Wrong API URL", apiEndpoint + "/session/version1/endpoint", apiURL.toExternalForm()); + + Mockito.verify(mockSyncInput, Mockito.times(1)).getPath(); + } }