Fix handling various GET and POST content types/logic for submitToken

- Properly support Form-encoded POST
- Fix #167
This commit is contained in:
Max Dor
2019-04-26 08:41:06 +02:00
parent 9d4680f55a
commit 39447b8b8b
10 changed files with 244 additions and 87 deletions

View File

@@ -31,6 +31,7 @@ import io.kamax.mxisd.proxy.Response;
import io.kamax.mxisd.util.RestClientUtils;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.form.FormData;
import io.undertow.util.HttpString;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
@@ -48,10 +49,7 @@ import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Map;
import java.util.Optional;
import java.util.*;
public abstract class BasicHttpHandler implements HttpHandler {
@@ -122,6 +120,20 @@ public abstract class BasicHttpHandler implements HttpHandler {
return GsonUtil.parseObj(getBodyUtf8(exchange));
}
protected String getOrThrow(FormData data, String key) {
FormData.FormValue value = data.getFirst(key);
if (Objects.isNull(value)) {
throw new IllegalArgumentException("Form key " + key + " is missing");
}
String object = value.getValue();
if (Objects.isNull(object)) {
throw new IllegalArgumentException("Form key " + key + " does not have a value");
}
return object;
}
protected void putHeader(HttpServerExchange ex, String name, String value) {
ex.getResponseHeaders().put(HttpString.tryFromString(name), value);
}

View File

@@ -20,94 +20,36 @@
package io.kamax.mxisd.http.undertow.handler.identity.v1;
import io.kamax.mxisd.config.ServerConfig;
import io.kamax.mxisd.config.ViewConfig;
import io.kamax.mxisd.http.IsAPIv1;
import io.kamax.mxisd.http.io.identity.SuccessStatusJson;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.kamax.mxisd.session.SessionManager;
import io.kamax.mxisd.session.ValidationResult;
import io.kamax.mxisd.util.FileUtil;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.HttpString;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
public class SessionValidateHandler extends BasicHttpHandler {
public abstract class SessionValidateHandler extends BasicHttpHandler {
public static final String Path = IsAPIv1.Base + "/validate/{medium}/submitToken";
private transient final Logger log = LoggerFactory.getLogger(SessionValidateHandler.class);
private SessionManager mgr;
private ServerConfig srvCfg;
private ViewConfig viewCfg;
public SessionValidateHandler(SessionManager mgr, ServerConfig srvCfg, ViewConfig viewCfg) {
public SessionValidateHandler(SessionManager mgr) {
this.mgr = mgr;
this.srvCfg = srvCfg;
this.viewCfg = viewCfg;
}
@Override
public void handleRequest(HttpServerExchange exchange) {
String medium = getQueryParameter(exchange, "medium");
String sid = getQueryParameter(exchange, "sid");
String secret = getQueryParameter(exchange, "client_secret");
String token = getQueryParameter(exchange, "token");
boolean isHtmlRequest = false;
for (String v : exchange.getRequestHeaders().get("Accept")) {
if (StringUtils.startsWithIgnoreCase(v, "text/html")) {
isHtmlRequest = true;
break;
}
protected ValidationResult handleRequest(String sid, String secret, String token) {
if (StringUtils.isEmpty(sid)) {
throw new IllegalArgumentException("sid is not set or is empty");
}
if (isHtmlRequest) {
handleHtmlRequest(exchange, medium, sid, secret, token);
} else {
handleJsonRequest(exchange, sid, secret, token);
if (StringUtils.isEmpty(secret)) {
throw new IllegalArgumentException("client secret is not set or is empty");
}
}
private void handleHtmlRequest(HttpServerExchange exchange, String medium, String sid, String secret, String token) {
log.info("Validating session {} for medium {}", sid, medium);
ValidationResult r = mgr.validate(sid, secret, token);
log.info("Session {} was validated", sid);
if (r.getNextUrl().isPresent()) {
String url = r.getNextUrl().get();
try {
url = new URL(url).toString();
} catch (MalformedURLException e) {
log.info("Session next URL {} is not a valid one, will prepend public URL {}", url, srvCfg.getPublicUrl());
url = srvCfg.getPublicUrl() + r.getNextUrl().get();
}
log.info("Session {} validation: next URL is present, redirecting to {}", sid, url);
exchange.setStatusCode(302);
exchange.getResponseHeaders().add(HttpString.tryFromString("Location"), url);
} else {
try {
String data = FileUtil.load(viewCfg.getSession().getOnTokenSubmit().getSuccess());
writeBodyAsUtf8(exchange, data);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
private void handleJsonRequest(HttpServerExchange exchange, String sid, String secret, String token) {
log.info("Requested: {}", exchange.getRequestURL());
mgr.validate(sid, secret, token);
log.info("Session {} was validated", sid);
respondJson(exchange, new SuccessStatusJson(true));
return mgr.validate(sid, secret, token);
}
}

View File

@@ -0,0 +1,82 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2019 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.http.undertow.handler.identity.v1;
import io.kamax.mxisd.config.MxisdConfig;
import io.kamax.mxisd.config.ServerConfig;
import io.kamax.mxisd.config.ViewConfig;
import io.kamax.mxisd.session.SessionManager;
import io.kamax.mxisd.session.ValidationResult;
import io.kamax.mxisd.util.FileUtil;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.HttpString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
public class SessionValidationGetHandler extends SessionValidateHandler {
private transient final Logger log = LoggerFactory.getLogger(SessionValidationGetHandler.class);
private ServerConfig srvCfg;
private ViewConfig viewCfg;
public SessionValidationGetHandler(SessionManager mgr, MxisdConfig cfg) {
super(mgr);
this.srvCfg = cfg.getServer();
this.viewCfg = cfg.getView();
}
@Override
public void handleRequest(HttpServerExchange exchange) {
log.info("Handling GET request to validate session");
String sid = getQueryParameter(exchange, "sid");
String secret = getQueryParameter(exchange, "client_secret");
String token = getQueryParameter(exchange, "token");
ValidationResult r = handleRequest(sid, secret, token);
log.info("Session {} was validated", sid);
if (r.getNextUrl().isPresent()) {
String url = r.getNextUrl().get();
try {
url = new URL(url).toString();
} catch (MalformedURLException e) {
log.info("Session next URL {} is not a valid one, will prepend public URL {}", url, srvCfg.getPublicUrl());
url = srvCfg.getPublicUrl() + r.getNextUrl().get();
}
log.info("Session {} validation: next URL is present, redirecting to {}", sid, url);
exchange.setStatusCode(302);
exchange.getResponseHeaders().add(HttpString.tryFromString("Location"), url);
} else {
try {
String data = FileUtil.load(viewCfg.getSession().getOnTokenSubmit().getSuccess());
writeBodyAsUtf8(exchange, data);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}

View File

@@ -0,0 +1,80 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2019 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.http.undertow.handler.identity.v1;
import com.google.gson.JsonObject;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.http.io.identity.SuccessStatusJson;
import io.kamax.mxisd.session.SessionManager;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.form.FormData;
import io.undertow.server.handlers.form.FormParserFactory;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
public class SessionValidationPostHandler extends SessionValidateHandler {
private transient final Logger log = LoggerFactory.getLogger(SessionValidationPostHandler.class);
private FormParserFactory factory;
public SessionValidationPostHandler(SessionManager mgr) {
super(mgr);
factory = FormParserFactory.builder().build();
}
@Override
public void handleRequest(HttpServerExchange exchange) throws IOException {
log.info("Handling POST request to validate session");
String sid;
String secret;
String token;
String contentType = getContentType(exchange).orElseThrow(() -> new IllegalArgumentException("Content type header is not set"));
if (StringUtils.equals(contentType, "application/json")) { // FIXME use MIME parsing tools
log.info("Parsing as JSON data");
JsonObject body = parseJsonObject(exchange);
sid = GsonUtil.getStringOrThrow(body, "sid");
secret = GsonUtil.getStringOrThrow(body, "client_secret");
token = GsonUtil.getStringOrThrow(body, "token");
} else if (StringUtils.equals(contentType, "application/x-www-form-urlencoded")) { // FIXME use MIME parsing tools
log.info("Parsing as Form data");
FormData data = factory.createParser(exchange).parseBlocking();
sid = getOrThrow(data, "sid");
secret = getOrThrow(data, "client_secret");
token = getOrThrow(data, "token");
} else {
log.info("Unsupported Content type: {}", contentType);
throw new IllegalArgumentException("Unsupported Content type: " + contentType);
}
handleRequest(sid, secret, token);
respondJson(exchange, new SuccessStatusJson(true));
}
}