Properly handle v1 of 3pid/bind
This commit is contained in:
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -30,6 +30,7 @@ import io.undertow.server.HttpHandler;
|
|||||||
import io.undertow.server.HttpServerExchange;
|
import io.undertow.server.HttpServerExchange;
|
||||||
import io.undertow.util.HttpString;
|
import io.undertow.util.HttpString;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -38,7 +39,10 @@ import java.io.UnsupportedEncodingException;
|
|||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Deque;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public abstract class BasicHttpHandler implements HttpHandler {
|
public abstract class BasicHttpHandler implements HttpHandler {
|
||||||
|
|
||||||
@@ -49,8 +53,16 @@ public abstract class BasicHttpHandler implements HttpHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected String getQueryParameter(HttpServerExchange exchange, String name) {
|
protected String getQueryParameter(HttpServerExchange exchange, String name) {
|
||||||
|
return getQueryParameter(exchange.getQueryParameters(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getQueryParameter(Map<String, Deque<String>> parms, String name) {
|
||||||
try {
|
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());
|
return URLDecoder.decode(raw, StandardCharsets.UTF_8.name());
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
throw new InternalServerError(e);
|
throw new InternalServerError(e);
|
||||||
@@ -61,22 +73,33 @@ public abstract class BasicHttpHandler implements HttpHandler {
|
|||||||
return getQueryParameter(exchange, name);
|
return getQueryParameter(exchange, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Optional<String> getContentType(HttpServerExchange exchange) {
|
||||||
|
return Optional.ofNullable(exchange.getRequestHeaders().getFirst("Content-Type"));
|
||||||
|
}
|
||||||
|
|
||||||
protected void writeBodyAsUtf8(HttpServerExchange exchange, String body) {
|
protected void writeBodyAsUtf8(HttpServerExchange exchange, String body) {
|
||||||
exchange.getResponseSender().send(body, StandardCharsets.UTF_8);
|
exchange.getResponseSender().send(body, StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T> T parseJsonTo(HttpServerExchange exchange, Class<T> type) {
|
protected String getBodyUtf8(HttpServerExchange exchange) {
|
||||||
try {
|
try {
|
||||||
return GsonUtil.get().fromJson(IOUtils.toString(exchange.getInputStream(), StandardCharsets.UTF_8), type);
|
return IOUtils.toString(exchange.getInputStream(), StandardCharsets.UTF_8);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected <T> T parseJsonTo(HttpServerExchange exchange, Class<T> type) {
|
||||||
|
return GsonUtil.get().fromJson(getBodyUtf8(exchange), type);
|
||||||
|
}
|
||||||
|
|
||||||
protected JsonObject parseJsonObject(HttpServerExchange exchange, String key) {
|
protected JsonObject parseJsonObject(HttpServerExchange exchange, String key) {
|
||||||
|
return GsonUtil.getObj(parseJsonObject(exchange), key);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected JsonObject parseJsonObject(HttpServerExchange exchange) {
|
||||||
try {
|
try {
|
||||||
JsonObject base = GsonUtil.parseObj(IOUtils.toString(exchange.getInputStream(), StandardCharsets.UTF_8));
|
return GsonUtil.parseObj(IOUtils.toString(exchange.getInputStream(), StandardCharsets.UTF_8));
|
||||||
return GsonUtil.getObj(base, key);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
@@ -23,14 +23,21 @@ package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
|||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import io.kamax.mxisd.exception.BadRequestException;
|
import io.kamax.mxisd.exception.BadRequestException;
|
||||||
import io.kamax.mxisd.http.IsAPIv1;
|
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.http.undertow.handler.BasicHttpHandler;
|
||||||
import io.kamax.mxisd.invitation.InvitationManager;
|
import io.kamax.mxisd.invitation.InvitationManager;
|
||||||
import io.kamax.mxisd.session.SessionMananger;
|
import io.kamax.mxisd.session.SessionMananger;
|
||||||
import io.undertow.server.HttpServerExchange;
|
import io.undertow.server.HttpServerExchange;
|
||||||
|
import io.undertow.util.QueryParameterUtils;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.apache.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class SessionTpidBindHandler extends BasicHttpHandler {
|
public class SessionTpidBindHandler extends BasicHttpHandler {
|
||||||
|
|
||||||
public static final String Path = IsAPIv1.Base + "/3pid/bind";
|
public static final String Path = IsAPIv1.Base + "/3pid/bind";
|
||||||
@@ -47,12 +54,27 @@ public class SessionTpidBindHandler extends BasicHttpHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleRequest(HttpServerExchange exchange) {
|
public void handleRequest(HttpServerExchange exchange) {
|
||||||
String sid = getQueryParameter(exchange, "sid");
|
BindRequest bindReq = new BindRequest();
|
||||||
String secret = getQueryParameter(exchange, "client_secret");
|
bindReq.setSid(getQueryParameter(exchange, BindRequest.Keys.SessionID));
|
||||||
String mxid = getQueryParameter(exchange, "mxid");
|
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<String, Deque<String>> 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 {
|
try {
|
||||||
mgr.bind(sid, secret, mxid);
|
mgr.bind(bindReq.getSid(), bindReq.getSecret(), bindReq.getUserId());
|
||||||
respond(exchange, new JsonObject());
|
respond(exchange, new JsonObject());
|
||||||
} catch (BadRequestException e) {
|
} catch (BadRequestException e) {
|
||||||
log.info("requested session was not validated");
|
log.info("requested session was not validated");
|
||||||
|
@@ -214,6 +214,10 @@ public class SessionMananger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void bind(String sid, String secret, String mxidRaw) {
|
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);
|
_MatrixID mxid = MatrixID.asAcceptable(mxidRaw);
|
||||||
ThreePidSession session = getSessionIfValidated(sid, secret);
|
ThreePidSession session = getSessionIfValidated(sid, secret);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user