package io.vertx.ext.auth.oauth2.impl;
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.*;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.vertx.ext.auth.oauth2.OAuth2ClientOptions;
import io.vertx.ext.auth.oauth2.OAuth2Response;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Map;
public class OAuth2API {
private static final Logger LOG = LoggerFactory.getLogger(OAuth2API.class);
public static void fetch(Vertx vertx, OAuth2ClientOptions config, HttpMethod method, String path, JsonObject headers, Buffer payload, Handler<AsyncResult<OAuth2Response>> callback) {
if (path == null || path.length() == 0) {
callback.handle(Future.failedFuture("Invalid path"));
return;
}
final String url = path.charAt(0) == '/' ? config.getSite() + path : path;
LOG.debug("Fetching URL: " + url);
final HttpClientRequest request = makeRequest(vertx, config, method, url, callback);
JsonObject tmp = config.getHeaders();
if (tmp != null) {
for (Map.Entry<String, Object> kv : tmp) {
request.putHeader(kv.getKey(), (String) kv.getValue());
}
}
if (headers != null) {
for (Map.Entry<String, Object> kv : headers) {
request.putHeader(kv.getKey(), (String) kv.getValue());
}
}
if (config.getUserAgent() != null) {
request.putHeader("User-Agent", config.getUserAgent());
}
if (payload != null) {
if (method == HttpMethod.POST || method == HttpMethod.PATCH || method == HttpMethod.PUT) {
request.putHeader("Content-Length", Integer.toString(payload.length()));
request.write(payload);
}
}
request.end();
}
public static HttpClientRequest makeRequest(Vertx vertx, HttpClientOptions options, HttpMethod method, String uri, final Handler<AsyncResult<OAuth2Response>> callback) {
HttpClient client;
try {
URL url = new URL(uri);
boolean isSecure = "https".equalsIgnoreCase(url.getProtocol());
String host = url.getHost();
int port = url.getPort();
if (port == -1) {
if (isSecure) {
port = 443;
} else {
port = 80;
}
}
client = vertx.createHttpClient(new HttpClientOptions(options)
.setSsl(isSecure)
.setDefaultHost(host)
.setDefaultPort(port));
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
final HttpClientRequest request = client.requestAbs(method, uri, resp -> {
resp.exceptionHandler(t -> {
callback.handle(Future.failedFuture(t));
client.close();
});
resp.bodyHandler(body -> {
if (resp.statusCode() < 200 || resp.statusCode() >= 300) {
if (body == null || body.length() == 0) {
callback.handle(Future.failedFuture(resp.statusMessage()));
} else {
callback.handle(Future.failedFuture(resp.statusMessage() + ": " + body.toString()));
}
} else {
callback.handle(Future.succeededFuture(new OAuth2ResponseImpl(resp.statusCode(), resp.headers(), body)));
}
client.close();
});
});
request.exceptionHandler(t -> {
callback.handle(Future.failedFuture(t));
client.close();
});
return request;
}
public static String stringify(JsonObject json) {
StringBuilder sb = new StringBuilder();
try {
for (Map.Entry<String, ?> kv : json) {
sb.append(URLEncoder.encode(kv.getKey(), "UTF-8"));
sb.append('=');
Object v = kv.getValue();
if (v != null) {
sb.append(URLEncoder.encode(v.toString(), "UTF-8"));
}
sb.append('&');
}
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
if (sb.length() > 0) {
sb.setLength(sb.length() - 1);
}
return sb.toString();
}
public static JsonObject queryToJSON(String query) throws UnsupportedEncodingException {
final JsonObject json = new JsonObject();
final String[] pairs = query.split("&");
for (String pair : pairs) {
final int idx = pair.indexOf("=");
final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), "UTF-8") : pair;
final String value = idx > 0 && pair.length() > idx + 1 ? URLDecoder.decode(pair.substring(idx + 1), "UTF-8") : null;
if (!json.containsKey(key)) {
json.put(key, value);
} else {
Object oldValue = json.getValue(key);
JsonArray array;
if (oldValue instanceof JsonArray) {
array = (JsonArray) oldValue;
} else {
array = new JsonArray();
array.add(oldValue);
json.put(key, array);
}
if (value == null) {
array.addNull();
} else {
array.add(value);
}
}
}
return json;
}
public static void processNonStandardHeaders(JsonObject json, OAuth2Response reply, String sep) {
final String xOAuthScopes = reply.getHeader("X-OAuth-Scopes");
final String xAcceptedOAuthScopes = reply.getHeader("X-Accepted-OAuth-Scopes");
if (xOAuthScopes != null) {
LOG.trace("Received non-standard X-OAuth-Scopes: "+ xOAuthScopes);
if (json.containsKey("scope")) {
json.put("scope", json.getString("scope") + sep + xOAuthScopes);
} else {
json.put("scope", xOAuthScopes);
}
}
if (xAcceptedOAuthScopes != null) {
LOG.trace("Received non-standard X-Accepted-OAuth-Scopes: "+ xAcceptedOAuthScopes);
json.put("acceptedScopes", xAcceptedOAuthScopes);
}
}
}