Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 25 additions & 7 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.bosh.client</groupId>
<groupId>de.evoila</groupId>
<artifactId>bosh-java-client</artifactId>
<version>0.9.1-SNAPSHOT</version>
<version>1.0.2-SNAPSHOT</version>
<name>BOSH Java Client</name>
<description>A Java client for the BOSH Director API.</description>
<url>https://github.com/davidehringer/bosh-java-client</url>
<url>https://github.com/evoila/bosh-java-client</url>

<organization>
<name>Cloud Foundry Community</name>
Expand All @@ -27,10 +27,10 @@
</licenses>

<scm>
<connection>scm:git:https://github.com/davidehringer/bosh-java-client.git</connection>
<developerConnection>scm:git:https://github.com/davidehringer/bosh-java-client.git</developerConnection>
<url>https://github.com/davidehringer/bosh-java-client.git</url>
<tag>HEAD</tag>
<connection>scm:git:https://github.com/evoila/bosh-java-client</connection>
<developerConnection>scm:git:https://github.com/evoila/bosh-java-client</developerConnection>
<url>https://github.com/evoila/bosh-java-client</url>
<tag>bosh-java-client-1.0.1</tag>
</scm>

<properties>
Expand Down Expand Up @@ -70,6 +70,11 @@
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
Expand Down Expand Up @@ -107,6 +112,19 @@
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.hierynomus</groupId>
<artifactId>sshj</artifactId>
<version>0.23.0</version>
</dependency>

<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.54</version>
</dependency>

</dependencies>

<build>
Expand Down
92 changes: 92 additions & 0 deletions src/main/java/io/bosh/client/OAuthCredentialsProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package io.bosh.client;

import io.bosh.client.authentication.Authentication;
import io.bosh.client.authentication.OAuth;
import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.ParseException;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.client.CredentialsProvider;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.client.token.AccessTokenProvider;
import org.springframework.security.oauth2.client.token.AccessTokenProviderChain;
import org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest;
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider;
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
import org.springframework.security.oauth2.client.token.grant.implicit.ImplicitAccessTokenProvider;
import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordAccessTokenProvider;
import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails;
import org.springframework.security.oauth2.common.AuthenticationScheme;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.util.Assert;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;

/**
* Created by jannikheyl on 09.02.18.
*/
public class OAuthCredentialsProvider implements Header {
private OAuth2AccessToken token;
private AccessTokenProviderChain CHAIN;
private ClientCredentialsResourceDetails credentials = new ClientCredentialsResourceDetails();


public OAuthCredentialsProvider(String host, String username,
String password, String scheme, OAuth auth) throws URISyntaxException {
if (!auth.isStrictHostKeyChecking()) {
CHAIN = new AccessTokenProviderChain(
Arrays.<AccessTokenProvider>asList(new UnsecureClientCredentialsAccessTokenProvider()));
} else {
CHAIN = new AccessTokenProviderChain(
Arrays.<AccessTokenProvider>asList(new ClientCredentialsAccessTokenProvider()));
}
URI uri = new URI(scheme + host + ":8443/oauth/token");

getCredentials(uri.toString(), username, password);
requestToken();
}

private void requestToken() {

if (token == null) {
token = CHAIN.obtainAccessToken(credentials, new DefaultAccessTokenRequest());
} else if (token.isExpired()) {
refreshAccessToken();
}

}

private void refreshAccessToken() {
Assert.notNull(token);

token = CHAIN.refreshAccessToken(credentials, token.getRefreshToken(), new DefaultAccessTokenRequest());
}

private ClientCredentialsResourceDetails getCredentials(String host, String username,
String password) {
credentials.setAccessTokenUri(host);
credentials.setClientAuthenticationScheme(AuthenticationScheme.form);
credentials.setClientId(username);
credentials.setClientSecret(password);
credentials.setGrantType("client_credentials");
return credentials;
}

@Override
public String getName() {
return "Authorization";
}

@Override
public String getValue() {
return token.getTokenType() + " " + token.getValue();
}

@Override
public HeaderElement[] getElements() throws ParseException {
return new HeaderElement[0];
}
}
33 changes: 33 additions & 0 deletions src/main/java/io/bosh/client/RequestLoggingInterceptor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.bosh.client;

import org.slf4j.LoggerFactory;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;

import java.io.IOException;
import java.nio.charset.Charset;

/**
* Created by jannikheyl on 12.02.18.
*/
public class RequestLoggingInterceptor implements ClientHttpRequestInterceptor {
private final static org.slf4j.Logger log = LoggerFactory.getLogger(RequestLoggingInterceptor.class);

@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
ClientHttpResponse response = execution.execute(request, body);

log.debug("request method: {}, request URI: {}, request headers: {}, request body: {}, response status code: {}, response headers: {}, response body: {}",
request.getMethod(),
request.getURI(),
request.getHeaders(),
new String(body, Charset.forName("UTF-8")),
response.getStatusCode(),
response.getHeaders(),
response.getBody());

return response;
}
}
5 changes: 5 additions & 0 deletions src/main/java/io/bosh/client/Scheme.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.bosh.client;

public enum Scheme {
https, http
}
6 changes: 3 additions & 3 deletions src/main/java/io/bosh/client/SpringDirectorClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
* @author David Ehringer
*/
public class SpringDirectorClient implements DirectorClient {

private final RestTemplate restTemplate;

private final Info info;
Expand All @@ -63,8 +63,8 @@ public class SpringDirectorClient implements DirectorClient {
this.jobs = new SpringJobs(restTemplate, root, tasks, deployments);
this.vms = new SpringVms(restTemplate, root, tasks);
}
public RestTemplate restTemplate(){

public RestTemplate restTemplate() {
return restTemplate;
}

Expand Down
80 changes: 55 additions & 25 deletions src/main/java/io/bosh/client/SpringDirectorClientBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
Expand All @@ -28,6 +29,10 @@

import javax.net.ssl.SSLContext;

import io.bosh.client.authentication.Authentication;
import io.bosh.client.authentication.BasicAuth;
import io.bosh.client.authentication.OAuth;
import org.apache.http.auth.AUTH;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
Expand All @@ -40,17 +45,14 @@
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.http.HttpRequest;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.*;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;


/**
* @author David Ehringer
*/
Expand All @@ -59,33 +61,45 @@ public class SpringDirectorClientBuilder {
private String host;
private String username;
private String password;

public SpringDirectorClientBuilder withCredentials(String username, String password){
private Authentication auth;
private Scheme scheme;
private Integer boshPort;

public SpringDirectorClientBuilder withCredentials(String username, String password, Authentication auth,
Scheme scheme, Integer boshPort) {
this.username = username;
this.password = password;
this.auth = auth;
this.scheme = scheme;
this.boshPort = boshPort;
return this;
}

public SpringDirectorClientBuilder withHost(String host){



public SpringDirectorClientBuilder withHost(String host) {
this.host = host;
return this;
}
public SpringDirectorClient build(){

public SpringDirectorClient build() {
// TODO validate
URI root = UriComponentsBuilder.newInstance().scheme("https").host(host).port(25555)
URI root = UriComponentsBuilder.newInstance().scheme(scheme.name()).host(host).port(boshPort)
.build().toUri();
RestTemplate restTemplate = new RestTemplate(createRequestFactory(host, username, password));
RestTemplate restTemplate = null;
try {
restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(createRequestFactory(host, username, password, auth, scheme, boshPort)));
} catch (URISyntaxException e) {
e.printStackTrace();
}
restTemplate.getInterceptors().add(new ContentTypeClientHttpRequestInterceptor());
restTemplate.getInterceptors().add(new RequestLoggingInterceptor());
handleTextHtmlResponses(restTemplate);
return new SpringDirectorClient(root, restTemplate);
}

private ClientHttpRequestFactory createRequestFactory(String host, String username,
String password) {
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(new AuthScope(host, 25555),
new UsernamePasswordCredentials(username, password));
String password, Authentication auth, Scheme scheme, Integer boshPort) throws URISyntaxException {

SSLContext sslContext = null;
try {
Expand All @@ -98,10 +112,26 @@ private ClientHttpRequestFactory createRequestFactory(String host, String userna
SSLConnectionSocketFactory connectionFactory = new SSLConnectionSocketFactory(sslContext,
new AllowAllHostnameVerifier());

// disabling redirect handling is critical for the way BOSH uses 302's
HttpClient httpClient = HttpClientBuilder.create().disableRedirectHandling()
.setDefaultCredentialsProvider(credentialsProvider)
.setSSLSocketFactory(connectionFactory).build();
HttpClient httpClient;

if (auth.getClass().equals(BasicAuth.class)) {
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(new AuthScope(host, boshPort),
new UsernamePasswordCredentials(username, password));

// disabling redirect handling is critical for the way BOSH uses 302's
httpClient = HttpClientBuilder.create().disableRedirectHandling()
.setDefaultCredentialsProvider(credentialsProvider)
.setSSLSocketFactory(connectionFactory).build();
} else {

// disabling redirect handling is critical for the way BOSH uses 302's
httpClient = HttpClientBuilder.create().disableRedirectHandling()
.setDefaultHeaders(Arrays.asList(new OAuthCredentialsProvider(host, username, password, scheme.name(), (OAuth) auth)))
.setSSLSocketFactory(connectionFactory).build();

}


return new HttpComponentsClientHttpRequestFactory(httpClient);
}
Expand All @@ -111,17 +141,17 @@ private void handleTextHtmlResponses(RestTemplate restTemplate) {
messageConverters.add(new StringHttpMessageConverter());
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
messageConverter.setSupportedMediaTypes(Arrays.asList(new MediaType("application", "json",
DEFAULT_CHARSET), new MediaType("application", "*+json", DEFAULT_CHARSET),
DEFAULT_CHARSET), new MediaType("application", "*+json", DEFAULT_CHARSET),
new MediaType("text", "html", DEFAULT_CHARSET)));
messageConverters.add(messageConverter);
restTemplate.setMessageConverters(messageConverters);
}

private static class ContentTypeClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {

@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException {
ClientHttpRequestExecution execution) throws IOException {
ClientHttpResponse response = execution.execute(request, body);
// some BOSH resources return text/plain and this modifies this response
// so we can use Jackson
Expand Down
Loading