Skip to content

Commit

Permalink
fix: java 17 authentication (#42)
Browse files Browse the repository at this point in the history
* fix: java 17 authentication

* use http headers constant

* add jdk17 to build configs
  • Loading branch information
butchyyyy authored Sep 8, 2023
1 parent a5e908a commit abefee5
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 29 deletions.
2 changes: 2 additions & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@ buildPlugin(
configurations: [
[platform: 'linux', jdk: 11],
[platform: 'windows', jdk: 11],
[platform: 'linux', jdk: 17],
[platform: 'windows', jdk: 17],
],
)
24 changes: 12 additions & 12 deletions src/main/java/jenkins/plugins/zulip/Zulip.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import java.net.http.HttpResponse;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
Expand Down Expand Up @@ -53,9 +52,9 @@ public Zulip(String url, String email, Secret apiKey) {
*
* @param httpClientBuilder The HttpClient builder
*/
protected void configureProxy(HttpClient.Builder httpClientBuilder) throws MalformedURLException {
protected void configureProxy(HttpClient.Builder httpClientBuilder, ProxyConfiguration proxyConfiguration)
throws MalformedURLException {
LOGGER.log(Level.FINE, "Setting up HttpClient proxy");
ProxyConfiguration proxyConfiguration = Jenkins.get().proxy;

if (proxyConfiguration != null && ZulipUtil.isValueSet(proxyConfiguration.name)) {
URL urlObj = new URL(url);
Expand All @@ -76,7 +75,7 @@ protected void configureProxy(HttpClient.Builder httpClientBuilder) throws Malfo
}
}

protected void configureAuthenticator(HttpClient.Builder httpClientBuilder) {
protected void configureAuthenticator(HttpClient.Builder httpClientBuilder, ProxyConfiguration proxyConfiguration) {
httpClientBuilder.authenticator(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
Expand All @@ -85,8 +84,6 @@ protected PasswordAuthentication getPasswordAuthentication() {

switch (getRequestorType()) {
case PROXY:
ProxyConfiguration proxyConfiguration = Jenkins.get().proxy;

if (ZulipUtil.isValueSet(proxyConfiguration.getUserName())) {
LOGGER.log(Level.FINE, "Using proxy authentication username: {0}, password: ******",
proxyConfiguration.getUserName());
Expand All @@ -97,6 +94,11 @@ protected PasswordAuthentication getPasswordAuthentication() {
LOGGER.log(Level.FINE, "Proxy authentication not configured in Jenkins");

return null;
case SERVER:
LOGGER.log(Level.FINE, "Using Zulip server authentication username: {0}, password: ******",
getEmail());

return new PasswordAuthentication(getEmail(), getApiKey().toCharArray());
default:
LOGGER.log(Level.FINE, "Unsupported authentication request");

Expand All @@ -107,9 +109,11 @@ protected PasswordAuthentication getPasswordAuthentication() {
}

protected HttpClient getClient() throws MalformedURLException {
ProxyConfiguration proxyConfiguration = Jenkins.get().proxy;

HttpClient.Builder httpClientBuilder = HttpClient.newBuilder();
configureProxy(httpClientBuilder);
configureAuthenticator(httpClientBuilder);
configureProxy(httpClientBuilder, proxyConfiguration);
configureAuthenticator(httpClientBuilder, proxyConfiguration);
return httpClientBuilder.build();
}

Expand Down Expand Up @@ -143,17 +147,13 @@ public HttpResponse<String> post(String method, Map<String, String> parameters)
.map(e -> encodeValue(e))
.collect(Collectors.joining("&"));

String auth_info = this.getEmail() + ":" + this.getApiKey();
String encoded_auth = Base64.getEncoder().encodeToString(auth_info.getBytes(encodingCharset));

HttpRequest httpRequest = HttpRequest.newBuilder()
.uri(getApiEndpoint(method))
// TODO: It would be nice if this version number read from the Maven XML file
// (which is possible, but annoying)
// http://stackoverflow.com/questions/8829147/maven-version-number-in-java-file
.header("User-Agent", "ZulipJenkins/0.1.2")
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Authorization", "Basic " + encoded_auth)
.POST(HttpRequest.BodyPublishers.ofString(body, encodingCharset))
.build();

Expand Down
63 changes: 46 additions & 17 deletions src/test/java/jenkins/plugins/zulip/ZulipTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.google.common.net.HttpHeaders;

import hudson.ProxyConfiguration;
import hudson.util.Secret;
import jenkins.model.Jenkins;

Expand All @@ -15,8 +16,9 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockserver.integration.ClientAndServer;
import org.mockserver.model.NottableString;
import org.mockserver.verify.VerificationTimes;

import static org.mockito.ArgumentMatchers.any;
import static org.mockserver.model.HttpRequest.request;
import static org.mockserver.model.HttpResponse.response;
import static org.mockserver.model.StringBody.exact;
Expand All @@ -28,9 +30,6 @@ public class ZulipTest {
@Mock
private Jenkins jenkins;

@Mock
private Secret secret;

@BeforeClass
public static void startMockServer() {
mockServer = ClientAndServer.startClientAndServer(1080);
Expand All @@ -42,62 +41,92 @@ public static void stopMockServer() {
}

private MockedStatic<Jenkins> jenkinsStatic;
private MockedStatic<Secret> secretStatic;

@Before
public void setUp() {
MockitoAnnotations.openMocks(this);

jenkinsStatic = Mockito.mockStatic(Jenkins.class);
jenkinsStatic.when(Jenkins::get).thenReturn(jenkins);

secretStatic = Mockito.mockStatic(Secret.class);
secretStatic.when(() -> Secret.toString(any(Secret.class))).thenReturn("secret");
}

@After
public void tearDown() {
jenkinsStatic.close();
secretStatic.close();
mockServer.reset();
jenkins.proxy = null;
}

@Test
public void testSendStreamMessage() throws Exception {
mockServer.reset();
mockServer.when(request().withPath("/api/v1/messages")).respond(response().withStatusCode(200));
Zulip zulip = new Zulip("http://localhost:1080", "[email protected]", secret);
Zulip zulip = new Zulip("http://localhost:1080", "[email protected]", Secret.fromString("secret"));
zulip.sendStreamMessage("testStreamůř", "testTopic", "testMessage");
mockServer.verify(
request()
.withMethod("POST")
.withPath("/api/v1/messages")
.withBody(exact(
"api-key=secret&subject=testTopic&to=testStream%C5%AF%C5%99&type=stream&email=jenkins-bot%40zulip.com&content=testMessage"))
.withHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded")
.withHeader(HttpHeaders.AUTHORIZATION, "Basic amVua2lucy1ib3RAenVsaXAuY29tOnNlY3JldA=="));
.withHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded"));
}

@Test
public void testFailGracefullyOnError() {
mockServer.reset();
mockServer.when(request().withPath("/api/v1/messages")).respond(response().withStatusCode(500));
Zulip zulip = new Zulip("http://localhost:1080", "[email protected]", secret);
Zulip zulip = new Zulip("http://localhost:1080", "[email protected]", Secret.fromString("secret"));
// Test that this does not throw exception
zulip.sendStreamMessage("testStream", "testTopic", "testMessage");
}

@Test
public void testFailGracefullyWhenUnreachable() {
Zulip zulip = new Zulip("http://localhost:1081", "[email protected]", secret);
Zulip zulip = new Zulip("http://localhost:1081", "[email protected]", Secret.fromString("secret"));
// Test that this does not throw exception
zulip.sendStreamMessage("testStream", "testTopic", "testMessage");
}

@Test
public void testFailGracefullyUnknonwHost() {
Zulip zulip = new Zulip("http://unreachable:1080", "[email protected]", secret);
Zulip zulip = new Zulip("http://unreachable:1080", "[email protected]", Secret.fromString("secret"));
// Test that this does not throw exception
zulip.sendStreamMessage("testStream", "testTopic", "testMessage");
}

@Test
public void testSendsAuthorizationWhenRequested() {
mockServer
.when(request().withPath("/api/v1/messages").withHeader(NottableString.not(HttpHeaders.AUTHORIZATION)))
.respond(response().withStatusCode(401).withHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic"));
mockServer.when(request().withPath("/api/v1/messages"))
.respond(response().withStatusCode(200));

Zulip zulip = new Zulip("http://localhost:1080", "[email protected]", Secret.fromString("secret"));
zulip.sendStreamMessage("testStream", "testTopic", "testMessage");

mockServer.verify(request(), VerificationTimes.exactly(2));
mockServer.verify(
request().withHeader(HttpHeaders.AUTHORIZATION, "Basic amVua2lucy1ib3RAenVsaXAuY29tOnNlY3JldA=="),
VerificationTimes.once());
}

@Test
public void testSendsProxyAuthorizationWhenRequested() {
jenkins.proxy = new ProxyConfiguration("localhost", 1080, "proxy-user", "proxy-password");

mockServer
.when(request().withPath("/api/v1/messages")
.withHeader(NottableString.not(HttpHeaders.PROXY_AUTHORIZATION)))
.respond(response().withStatusCode(407).withHeader(HttpHeaders.PROXY_AUTHENTICATE, "Basic"));
mockServer.when(request().withPath("/api/v1/messages")).respond(response().withStatusCode(200));

Zulip zulip = new Zulip("http://localhost:5000", "[email protected]", Secret.fromString("secret"));
zulip.sendStreamMessage("testStream", "testTopic", "testMessage");

mockServer.verify(request(), VerificationTimes.exactly(2));
mockServer.verify(
request().withHeader(HttpHeaders.PROXY_AUTHORIZATION, "Basic cHJveHktdXNlcjpwcm94eS1wYXNzd29yZA=="),
VerificationTimes.once());
}

}

0 comments on commit abefee5

Please sign in to comment.