diff --git a/client/src/main/java/org/projectodd/openwhisk/Activations.java b/client/src/main/java/org/projectodd/openwhisk/Activations.java new file mode 100644 index 0000000..19c63fe --- /dev/null +++ b/client/src/main/java/org/projectodd/openwhisk/Activations.java @@ -0,0 +1,52 @@ +package org.projectodd.openwhisk; + +import org.projectodd.openwhisk.api.ActivationsApi; +import org.projectodd.openwhisk.invoker.ApiException; + +public class Activations { + + private final ActivationsApi activationsApi; + private final String namespace; + + Activations(OWskClient client) { + if (client == null) { + throw new IllegalArgumentException( + "Null passed as client to " + + Activations.class.getCanonicalName() + + " constructor"); + } + if (client.getClient() == null) { + throw new IllegalArgumentException( + "Null apiClient inside " + + OWskClient.class.getCanonicalName()); + } + if (client.getConfiguration() == null) { + throw new IllegalArgumentException( + "Null configuration inside " + + OWskClient.class.getCanonicalName()); + } + if (client.getConfiguration().getNamespace() == null + || client.getConfiguration().getNamespace() + .isEmpty()) { + throw new IllegalArgumentException( + "Missing namespace inside client configuration"); + } + this.activationsApi = new ActivationsApi(client.getClient()); + this.namespace = client.getConfiguration().getNamespace(); + } + + /** + * Get an activation's result + */ + public Object getActivationResult(String activationId) + throws ApiException { + if (activationId == null + || activationId.isEmpty()) { + throw new IllegalArgumentException( + "Missing activation id"); + } + return activationsApi + .namespacesNamespaceActivationsActivationidResultGet( + namespace, activationId).getResult(); + } +} diff --git a/client/src/main/java/org/projectodd/openwhisk/Configuration.java b/client/src/main/java/org/projectodd/openwhisk/Configuration.java index a5a980e..ac783c6 100644 --- a/client/src/main/java/org/projectodd/openwhisk/Configuration.java +++ b/client/src/main/java/org/projectodd/openwhisk/Configuration.java @@ -3,6 +3,8 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; import java.util.Properties; public class Configuration { @@ -20,6 +22,10 @@ public class Configuration { private String host = "localhost"; private int port = DEFAULT_PORT; private String auth; + private boolean useOauth; + private String oauthTokenUrl; + private Map oauthTokenRequestParameters; + private Map oauthTokenRequestHeaders; private String namespace = DEFAULT_NAMESPACE; private String actionPackage = ""; private boolean debugging = false; @@ -29,11 +35,19 @@ public class Configuration { private Configuration() { } - private Configuration(final String host, final int port, final String auth, final String namespace, final String actionPackage, + private Configuration(final String host, final int port, final String auth, final boolean useOauth, + final String oauthTokenUrl, + final Map oauthTokenRequestParameters, + final Map oauthTokenRequestHeaders, + final String namespace, final String actionPackage, boolean debugging, boolean insecure, final int timeout) { this.host = host; this.port = port; this.auth = auth; + this.useOauth = useOauth; + this.oauthTokenUrl = oauthTokenUrl; + this.oauthTokenRequestParameters = oauthTokenRequestParameters; + this.oauthTokenRequestHeaders = oauthTokenRequestHeaders; this.namespace = namespace; this.actionPackage = actionPackage; this.debugging = debugging; @@ -61,6 +75,22 @@ public String getAuth() { return auth; } + public boolean useOauth() { + return useOauth; + } + + public String getOauthTokenUrl() { + return oauthTokenUrl; + } + + public Map getOauthTokenRequestParameters() { + return new HashMap<>(oauthTokenRequestParameters); + } + + public Map getOauthTokenRequestHeaders() { + return new HashMap<>(oauthTokenRequestHeaders); + } + public String getNamespace() { return namespace; } @@ -113,6 +143,10 @@ public static class Builder { private int port = DEFAULT_PORT; private int timeout = DEFAULT_TIMEOUT; private String auth; + private boolean useOauth; + private String oauthTokenUrl; + private Map oauthTokenRequestParameters; + private Map oauthTokenRequestHeaders; private String namespace = DEFAULT_NAMESPACE; private String actionPackage = DEFAULT_ACTION_PACKAGE; @@ -123,6 +157,10 @@ public Builder(final Configuration configuration) { host = configuration.host; port = configuration.port; auth = configuration.auth; + useOauth = configuration.useOauth; + oauthTokenUrl = configuration.oauthTokenUrl; + oauthTokenRequestParameters = configuration.oauthTokenRequestParameters; + oauthTokenRequestHeaders = configuration.oauthTokenRequestHeaders; namespace = configuration.namespace; actionPackage = configuration.actionPackage; insecure = configuration.insecure; @@ -157,6 +195,30 @@ public Builder auth(String auth) { return this; } + public Builder useOauth(boolean useOauth) { + this.useOauth = useOauth; + return this; + } + + public Builder oauthTokenUrl(String oauthTokenUrl) { + this.oauthTokenUrl = oauthTokenUrl; + return this; + } + + public Builder oauthTokenRequestParameters( + Map requestParameters) { + this.oauthTokenRequestParameters = new HashMap<>( + requestParameters); + return this; + } + + public Builder oauthTokenRequestHeaders( + Map requestHeaders) { + this.oauthTokenRequestHeaders = new HashMap<>( + requestHeaders); + return this; + } + public Builder namespace(String namespace) { this.namespace = namespace; return this; @@ -168,7 +230,14 @@ public Builder actionPackage(String actionPackage) { } public Configuration build() { - return new Configuration(host, port, auth, namespace, actionPackage, debugging, insecure, timeout); + if (useOauth && (oauthTokenUrl == null + || oauthTokenUrl.isEmpty())) { + throw new IllegalStateException( + "No URL provided to request Oauth token"); + } + return new Configuration(host, port, auth, useOauth, oauthTokenUrl, + oauthTokenRequestParameters, oauthTokenRequestHeaders, + namespace, actionPackage, debugging, insecure, timeout); } } } diff --git a/client/src/main/java/org/projectodd/openwhisk/OWskClient.java b/client/src/main/java/org/projectodd/openwhisk/OWskClient.java index 8293823..7ed6f77 100644 --- a/client/src/main/java/org/projectodd/openwhisk/OWskClient.java +++ b/client/src/main/java/org/projectodd/openwhisk/OWskClient.java @@ -7,15 +7,17 @@ import static java.nio.charset.StandardCharsets.ISO_8859_1; import static okio.ByteString.encodeString; +import java.io.IOException; + public class OWskClient { private ApiClient client; private Configuration configuration; - public OWskClient() { + public OWskClient() throws IOException { configure(Configuration.load()); } - public void configure(final Configuration configuration) { + public void configure(final Configuration configuration) throws IOException { this.configuration = configuration; client = new ApiClient(); client.setBasePath(format("https://%s:%s/api/v1", configuration.getHost(), configuration.getPort())); @@ -25,7 +27,19 @@ public void configure(final Configuration configuration) { client.setVerifyingSsl(!configuration.isInsecure()); if(getConfiguration().getAuth()!= null) { - client.addDefaultHeader("Authorization", "Basic " + encodeString(getConfiguration().getAuth(), ISO_8859_1).base64()); + if (configuration.useOauth()) { + OauthManager oauthManager = new OauthManager( + configuration.getOauthTokenUrl(), + configuration.getOauthTokenRequestParameters(), + configuration.getOauthTokenRequestHeaders()); + client.getHttpClient().interceptors().add(oauthManager); + client.getHttpClient().setAuthenticator(oauthManager); + } else { + client.addDefaultHeader( + "Authorization", + "Basic " + encodeString(getConfiguration().getAuth(), ISO_8859_1) + .base64()); + } } client.setUserAgent("Incubating Apache OpenWhisk Java client"); client.setDebugging(configuration.isDebugging()); @@ -42,4 +56,8 @@ ApiClient getClient() { public Actions actions() { return new Actions(this); } + + public Activations activations() { + return new Activations(this); + } } diff --git a/client/src/main/java/org/projectodd/openwhisk/OauthManager.java b/client/src/main/java/org/projectodd/openwhisk/OauthManager.java new file mode 100644 index 0000000..3985579 --- /dev/null +++ b/client/src/main/java/org/projectodd/openwhisk/OauthManager.java @@ -0,0 +1,97 @@ +package org.projectodd.openwhisk; + +import java.io.IOException; +import java.net.Proxy; +import java.util.Map; +import java.util.Map.Entry; + + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.squareup.okhttp.Authenticator; +import com.squareup.okhttp.Interceptor; +import com.squareup.okhttp.MediaType; +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.RequestBody; +import com.squareup.okhttp.Response; + + +public class OauthManager implements Interceptor, Authenticator { + + private final OkHttpClient clientForToken = new OkHttpClient(); + private final JsonParser jsonParser = new JsonParser(); + private final String tokenUrl; + private final Map formParameters; + private final Map headers; + + private String token; + + public OauthManager(String tokenUrl, + Map formParameters, + Map headers) throws IOException { + this.tokenUrl = tokenUrl; + this.formParameters = formParameters; + this.headers = headers; + token = getNewToken(); + } + + private String getNewToken() throws IOException { + + StringBuilder bodyBuilder = new StringBuilder(); + for (Entry parameter : formParameters.entrySet()) { + if (bodyBuilder.length() > 0) { + bodyBuilder.append("&"); + } + bodyBuilder.append(parameter.getKey() + "=" + + parameter.getValue()); + } + Request.Builder requestBuilder = new Request.Builder() + .url(tokenUrl) + .post(RequestBody.create( + MediaType.parse("application/x-www-form-urlencoded"), + bodyBuilder.toString())); + + for (Entry header : headers.entrySet()) { + requestBuilder.header(header.getKey(), header.getValue()); + } + requestBuilder.header("Accept", "application/json"); + requestBuilder.header("Content-Type", + "application/x-www-form-urlencoded"); + Request request = requestBuilder.build(); + + Response response = clientForToken.newCall(request).execute(); + JsonObject parsedResponse = jsonParser.parse( + response.body().string()).getAsJsonObject(); + return parsedResponse.get("access_token").getAsString(); + } + + @Override + public Response intercept(Chain chain) throws IOException { + Request request = chain.request() + .newBuilder() + .addHeader("Authorization", "Bearer " + token) + .build(); + return chain.proceed(request); + } + + @Override + public Request authenticate(Proxy route, Response response) throws IOException { + if (!response.request().header("Authorization").equals(token)) { + return null; + } + token = getNewToken(); + if (token != null) { + return response.request().newBuilder() + .header("Authorization", "Bearer " + token) + .build(); + } else { + return null; + } + } + + @Override + public Request authenticateProxy(Proxy arg0, Response arg1) throws IOException { + return authenticate(arg0, arg1); + } +}