diff --git a/src/main/java/io/kamax/mxisd/HttpMxisd.java b/src/main/java/io/kamax/mxisd/HttpMxisd.java
index 0ee9d9b..26a4426 100644
--- a/src/main/java/io/kamax/mxisd/HttpMxisd.java
+++ b/src/main/java/io/kamax/mxisd/HttpMxisd.java
@@ -73,7 +73,6 @@ public class HttpMxisd {
HttpHandler asNotFoundHandler = SaneHandler.around(new AsNotFoundHandler(m.getAs()));
HttpHandler storeInvHandler = SaneHandler.around(new StoreInviteHandler(m.getConfig().getServer(), m.getInvite(), m.getKeyManager()));
- HttpHandler sessValidateHandler = SaneHandler.around(new SessionValidateHandler(m.getSession(), m.getConfig().getServer(), m.getConfig().getView()));
httpSrv = Undertow.builder().addHttpListener(m.getConfig().getServer().getPort(), "0.0.0.0").setHandler(Handlers.routing()
@@ -103,8 +102,8 @@ public class HttpMxisd {
.post(BulkLookupHandler.Path, SaneHandler.around(new BulkLookupHandler(m.getIdentity())))
.post(StoreInviteHandler.Path, storeInvHandler)
.post(SessionStartHandler.Path, SaneHandler.around(new SessionStartHandler(m.getSession())))
- .get(SessionValidateHandler.Path, sessValidateHandler)
- .post(SessionValidateHandler.Path, sessValidateHandler)
+ .get(SessionValidateHandler.Path, SaneHandler.around(new SessionValidationGetHandler(m.getSession(), m.getConfig())))
+ .post(SessionValidateHandler.Path, SaneHandler.around(new SessionValidationPostHandler(m.getSession())))
.get(SessionTpidGetValidatedHandler.Path, SaneHandler.around(new SessionTpidGetValidatedHandler(m.getSession())))
.post(SessionTpidBindHandler.Path, SaneHandler.around(new SessionTpidBindHandler(m.getSession(), m.getInvite())))
.post(SessionTpidUnbindHandler.Path, SaneHandler.around(new SessionTpidUnbindHandler(m.getSession())))
diff --git a/src/main/java/io/kamax/mxisd/http/undertow/handler/BasicHttpHandler.java b/src/main/java/io/kamax/mxisd/http/undertow/handler/BasicHttpHandler.java
index fa059f8..26e1eeb 100644
--- a/src/main/java/io/kamax/mxisd/http/undertow/handler/BasicHttpHandler.java
+++ b/src/main/java/io/kamax/mxisd/http/undertow/handler/BasicHttpHandler.java
@@ -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);
}
diff --git a/src/main/java/io/kamax/mxisd/http/undertow/handler/identity/v1/SessionValidateHandler.java b/src/main/java/io/kamax/mxisd/http/undertow/handler/identity/v1/SessionValidateHandler.java
index 589d904..00dcd90 100644
--- a/src/main/java/io/kamax/mxisd/http/undertow/handler/identity/v1/SessionValidateHandler.java
+++ b/src/main/java/io/kamax/mxisd/http/undertow/handler/identity/v1/SessionValidateHandler.java
@@ -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);
}
}
diff --git a/src/main/java/io/kamax/mxisd/http/undertow/handler/identity/v1/SessionValidationGetHandler.java b/src/main/java/io/kamax/mxisd/http/undertow/handler/identity/v1/SessionValidationGetHandler.java
new file mode 100644
index 0000000..f9294a2
--- /dev/null
+++ b/src/main/java/io/kamax/mxisd/http/undertow/handler/identity/v1/SessionValidationGetHandler.java
@@ -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 .
+ */
+
+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);
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/io/kamax/mxisd/http/undertow/handler/identity/v1/SessionValidationPostHandler.java b/src/main/java/io/kamax/mxisd/http/undertow/handler/identity/v1/SessionValidationPostHandler.java
new file mode 100644
index 0000000..7ccdcec
--- /dev/null
+++ b/src/main/java/io/kamax/mxisd/http/undertow/handler/identity/v1/SessionValidationPostHandler.java
@@ -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 .
+ */
+
+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));
+ }
+
+}
diff --git a/src/main/java/io/kamax/mxisd/session/SessionManager.java b/src/main/java/io/kamax/mxisd/session/SessionManager.java
index 66e5d11..aea6eb1 100644
--- a/src/main/java/io/kamax/mxisd/session/SessionManager.java
+++ b/src/main/java/io/kamax/mxisd/session/SessionManager.java
@@ -38,8 +38,8 @@ import io.kamax.mxisd.notification.NotificationManager;
import io.kamax.mxisd.storage.IStorage;
import io.kamax.mxisd.storage.dao.IThreePidSessionDao;
import io.kamax.mxisd.threepid.session.ThreePidSession;
-import org.apache.commons.lang.RandomStringUtils;
-import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.commons.lang3.StringUtils;
import org.apache.http.impl.client.CloseableHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -139,12 +139,13 @@ public class SessionManager {
}
public ValidationResult validate(String sid, String secret, String token) {
+ log.info("Validating session {}", sid);
ThreePidSession session = getSession(sid, secret);
- log.info("Attempting validation for session {} from {}", session.getId(), session.getServer());
+ log.info("Session {} is from {}", session.getId(), session.getServer());
session.validate(token);
storage.updateThreePidSession(session.getDao());
- log.info("Session {} has been validated locally", session.getId());
+ log.info("Session {} has been validated", session.getId());
ValidationResult r = new ValidationResult(session);
session.getNextLink().ifPresent(r::setNextUrl);
diff --git a/src/main/java/io/kamax/mxisd/threepid/connector/email/BlackholeEmailConnector.java b/src/main/java/io/kamax/mxisd/threepid/connector/email/BlackholeEmailConnector.java
new file mode 100644
index 0000000..0c862e6
--- /dev/null
+++ b/src/main/java/io/kamax/mxisd/threepid/connector/email/BlackholeEmailConnector.java
@@ -0,0 +1,37 @@
+/*
+ * 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 .
+ */
+
+package io.kamax.mxisd.threepid.connector.email;
+
+public class BlackholeEmailConnector implements EmailConnector {
+
+ public static final String ID = "none";
+
+ @Override
+ public String getId() {
+ return ID;
+ }
+
+ @Override
+ public void send(String senderAddress, String senderName, String recipient, String content) {
+ //dev/null
+ }
+
+}
diff --git a/src/main/java/io/kamax/mxisd/threepid/connector/email/BuiltInEmailConnectorSupplier.java b/src/main/java/io/kamax/mxisd/threepid/connector/email/BuiltInEmailConnectorSupplier.java
index f2b6d99..f886287 100644
--- a/src/main/java/io/kamax/mxisd/threepid/connector/email/BuiltInEmailConnectorSupplier.java
+++ b/src/main/java/io/kamax/mxisd/threepid/connector/email/BuiltInEmailConnectorSupplier.java
@@ -33,6 +33,10 @@ public class BuiltInEmailConnectorSupplier implements EmailConnectorSupplier {
@Override
public Optional apply(EmailConfig cfg, Mxisd mxisd) {
+ if (StringUtils.equals(BlackholeEmailConnector.ID, cfg.getConnector())) {
+ return Optional.of(new BlackholeEmailConnector());
+ }
+
if (StringUtils.equals(EmailSmtpConnector.ID, cfg.getConnector())) {
EmailSmtpConfig smtpCfg = GsonUtil.get().fromJson(cfg.getConnectors().getOrDefault(EmailSmtpConnector.ID, new JsonObject()), EmailSmtpConfig.class);
return Optional.of(new EmailSmtpConnector(smtpCfg));
diff --git a/src/main/java/io/kamax/mxisd/threepid/connector/phone/BlackholePhoneConnector.java b/src/main/java/io/kamax/mxisd/threepid/connector/phone/BlackholePhoneConnector.java
index b9378cd..14c243d 100644
--- a/src/main/java/io/kamax/mxisd/threepid/connector/phone/BlackholePhoneConnector.java
+++ b/src/main/java/io/kamax/mxisd/threepid/connector/phone/BlackholePhoneConnector.java
@@ -24,14 +24,14 @@ public class BlackholePhoneConnector implements PhoneConnector {
public static final String ID = "none";
- @Override
- public void send(String recipient, String content) {
- //dev/null
- }
-
@Override
public String getId() {
return ID;
}
+ @Override
+ public void send(String recipient, String content) {
+ //dev/null
+ }
+
}
diff --git a/src/main/java/io/kamax/mxisd/threepid/connector/phone/BuiltInPhoneConnectorSupplier.java b/src/main/java/io/kamax/mxisd/threepid/connector/phone/BuiltInPhoneConnectorSupplier.java
index d00bcf6..355316f 100644
--- a/src/main/java/io/kamax/mxisd/threepid/connector/phone/BuiltInPhoneConnectorSupplier.java
+++ b/src/main/java/io/kamax/mxisd/threepid/connector/phone/BuiltInPhoneConnectorSupplier.java
@@ -33,15 +33,15 @@ public class BuiltInPhoneConnectorSupplier implements PhoneConnectorSupplier {
@Override
public Optional apply(PhoneConfig cfg, Mxisd mxisd) {
+ if (StringUtils.equals(BlackholePhoneConnector.ID, cfg.getConnector())) {
+ return Optional.of(new BlackholePhoneConnector());
+ }
+
if (StringUtils.equals(PhoneSmsTwilioConnector.ID, cfg.getConnector())) {
PhoneTwilioConfig cCfg = GsonUtil.get().fromJson(cfg.getConnectors().getOrDefault(PhoneSmsTwilioConnector.ID, new JsonObject()), PhoneTwilioConfig.class);
return Optional.of(new PhoneSmsTwilioConnector(cCfg));
}
- if (StringUtils.equals(BlackholePhoneConnector.ID, cfg.getConnector())) {
- return Optional.of(new BlackholePhoneConnector());
- }
-
return Optional.empty();
}