Skip to content

Commit

Permalink
test: add tests for new post action logic
Browse files Browse the repository at this point in the history
  • Loading branch information
at88mph committed Oct 17, 2024
1 parent 6d52ec8 commit f6461d4
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 40 deletions.
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
104 changes: 64 additions & 40 deletions src/main/java/org/opencadc/scienceportal/session/PostAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<String, Object> 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();
Expand All @@ -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<String, Object> 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();
}
}
122 changes: 122 additions & 0 deletions src/test/java/org/opencadc/scienceportal/session/PostActionTest.java
Original file line number Diff line number Diff line change
@@ -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<String> 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<String> 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<String> 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<String, Object> 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<HttpRequestProperty> 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();
}
}

0 comments on commit f6461d4

Please sign in to comment.