Fix handling various GET and POST content types/logic for submitToken
- Properly support Form-encoded POST - Fix #167
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user