diff --git a/src/main/java/io/kamax/mxisd/http/io/identity/BindRequest.java b/src/main/java/io/kamax/mxisd/http/io/identity/BindRequest.java new file mode 100644 index 0000000..031f6c9 --- /dev/null +++ b/src/main/java/io/kamax/mxisd/http/io/identity/BindRequest.java @@ -0,0 +1,67 @@ +/* + * 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.io.identity; + +import com.google.gson.annotations.SerializedName; + +public class BindRequest { + + public static class Keys { + + public static final String SessionID = "sid"; + public static final String Secret = "client_secret"; + public static final String UserID = "mxid"; + } + + @SerializedName(Keys.SessionID) + private String sid; + + @SerializedName(Keys.Secret) + private String secret; + + @SerializedName(Keys.UserID) + private String userId; + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + + public String getSecret() { + return secret; + } + + public void setSecret(String secret) { + this.secret = secret; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + +} 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 884c0c4..f4740ce 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 @@ -30,6 +30,7 @@ import io.undertow.server.HttpHandler; import io.undertow.server.HttpServerExchange; import io.undertow.util.HttpString; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,7 +39,10 @@ import java.io.UnsupportedEncodingException; import java.net.InetSocketAddress; 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; public abstract class BasicHttpHandler implements HttpHandler { @@ -49,8 +53,16 @@ public abstract class BasicHttpHandler implements HttpHandler { } protected String getQueryParameter(HttpServerExchange exchange, String name) { + return getQueryParameter(exchange.getQueryParameters(), name); + } + + protected String getQueryParameter(Map> parms, String name) { try { - String raw = exchange.getQueryParameters().getOrDefault(name, new LinkedList<>()).peekFirst(); + String raw = parms.getOrDefault(name, new LinkedList<>()).peekFirst(); + if (StringUtils.isEmpty(raw)) { + return raw; + } + return URLDecoder.decode(raw, StandardCharsets.UTF_8.name()); } catch (UnsupportedEncodingException e) { throw new InternalServerError(e); @@ -61,22 +73,33 @@ public abstract class BasicHttpHandler implements HttpHandler { return getQueryParameter(exchange, name); } + protected Optional getContentType(HttpServerExchange exchange) { + return Optional.ofNullable(exchange.getRequestHeaders().getFirst("Content-Type")); + } + protected void writeBodyAsUtf8(HttpServerExchange exchange, String body) { exchange.getResponseSender().send(body, StandardCharsets.UTF_8); } - protected T parseJsonTo(HttpServerExchange exchange, Class type) { + protected String getBodyUtf8(HttpServerExchange exchange) { try { - return GsonUtil.get().fromJson(IOUtils.toString(exchange.getInputStream(), StandardCharsets.UTF_8), type); + return IOUtils.toString(exchange.getInputStream(), StandardCharsets.UTF_8); } catch (IOException e) { throw new RuntimeException(e); } } + protected T parseJsonTo(HttpServerExchange exchange, Class type) { + return GsonUtil.get().fromJson(getBodyUtf8(exchange), type); + } + protected JsonObject parseJsonObject(HttpServerExchange exchange, String key) { + return GsonUtil.getObj(parseJsonObject(exchange), key); + } + + protected JsonObject parseJsonObject(HttpServerExchange exchange) { try { - JsonObject base = GsonUtil.parseObj(IOUtils.toString(exchange.getInputStream(), StandardCharsets.UTF_8)); - return GsonUtil.getObj(base, key); + return GsonUtil.parseObj(IOUtils.toString(exchange.getInputStream(), StandardCharsets.UTF_8)); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/src/main/java/io/kamax/mxisd/http/undertow/handler/identity/v1/SessionTpidBindHandler.java b/src/main/java/io/kamax/mxisd/http/undertow/handler/identity/v1/SessionTpidBindHandler.java index 69472fe..943f518 100644 --- a/src/main/java/io/kamax/mxisd/http/undertow/handler/identity/v1/SessionTpidBindHandler.java +++ b/src/main/java/io/kamax/mxisd/http/undertow/handler/identity/v1/SessionTpidBindHandler.java @@ -23,14 +23,21 @@ package io.kamax.mxisd.http.undertow.handler.identity.v1; import com.google.gson.JsonObject; import io.kamax.mxisd.exception.BadRequestException; import io.kamax.mxisd.http.IsAPIv1; +import io.kamax.mxisd.http.io.identity.BindRequest; import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler; import io.kamax.mxisd.invitation.InvitationManager; import io.kamax.mxisd.session.SessionMananger; import io.undertow.server.HttpServerExchange; +import io.undertow.util.QueryParameterUtils; +import org.apache.commons.lang.StringUtils; import org.apache.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.nio.charset.StandardCharsets; +import java.util.Deque; +import java.util.Map; + public class SessionTpidBindHandler extends BasicHttpHandler { public static final String Path = IsAPIv1.Base + "/3pid/bind"; @@ -47,12 +54,27 @@ public class SessionTpidBindHandler extends BasicHttpHandler { @Override public void handleRequest(HttpServerExchange exchange) { - String sid = getQueryParameter(exchange, "sid"); - String secret = getQueryParameter(exchange, "client_secret"); - String mxid = getQueryParameter(exchange, "mxid"); + BindRequest bindReq = new BindRequest(); + bindReq.setSid(getQueryParameter(exchange, BindRequest.Keys.SessionID)); + bindReq.setSecret(getQueryParameter(exchange, BindRequest.Keys.Secret)); + bindReq.setUserId(getQueryParameter(exchange, BindRequest.Keys.UserID)); + + String reqContentType = getContentType(exchange).orElse("application/octet-stream"); + if (StringUtils.equals("application/x-www-form-urlencoded", reqContentType)) { + String body = getBodyUtf8(exchange); + Map> parms = QueryParameterUtils.parseQueryString(body, StandardCharsets.UTF_8.name()); + bindReq.setSid(getQueryParameter(parms, BindRequest.Keys.SessionID)); + bindReq.setSecret(getQueryParameter(parms, BindRequest.Keys.Secret)); + bindReq.setUserId(getQueryParameter(parms, BindRequest.Keys.UserID)); + } else if (StringUtils.equals("application/json", reqContentType)) { + bindReq = parseJsonTo(exchange, BindRequest.class); + } else { + log.warn("Unknown encoding in 3PID session bind: {}", reqContentType); + log.warn("The request will most likely fail"); + } try { - mgr.bind(sid, secret, mxid); + mgr.bind(bindReq.getSid(), bindReq.getSecret(), bindReq.getUserId()); respond(exchange, new JsonObject()); } catch (BadRequestException e) { log.info("requested session was not validated"); diff --git a/src/main/java/io/kamax/mxisd/session/SessionMananger.java b/src/main/java/io/kamax/mxisd/session/SessionMananger.java index d9f5d9f..5f81898 100644 --- a/src/main/java/io/kamax/mxisd/session/SessionMananger.java +++ b/src/main/java/io/kamax/mxisd/session/SessionMananger.java @@ -214,6 +214,10 @@ public class SessionMananger { } public void bind(String sid, String secret, String mxidRaw) { + if (StringUtils.isEmpty(mxidRaw)) { + throw new IllegalArgumentException("No Matrix User ID provided"); + } + _MatrixID mxid = MatrixID.asAcceptable(mxidRaw); ThreePidSession session = getSessionIfValidated(sid, secret);