Properly handle /v1/store-invite
This commit is contained in:
@@ -0,0 +1,179 @@
|
|||||||
|
/*
|
||||||
|
* 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 StoreInviteRequest {
|
||||||
|
|
||||||
|
// Available keys from Spec + HS implementations reverse-engineering
|
||||||
|
//
|
||||||
|
// Synapse: https://github.com/matrix-org/synapse/blob/a219ce87263ad9be887cf039a04b4a1f06b7b0b8/synapse/handlers/room_member.py#L826
|
||||||
|
public static class Keys {
|
||||||
|
|
||||||
|
public static final String Medium = "medium";
|
||||||
|
public static final String Address = "address";
|
||||||
|
public static final String RoomID = "room_id";
|
||||||
|
public static final String RoomAlias = "room_alias"; // Not in the spec, arbitrary
|
||||||
|
public static final String RoomAvatarURL = "room_avatar_url"; // Not in the spec, arbitrary
|
||||||
|
public static final String RoomJoinRules = "room_join_rules"; // Not in the spec, arbitrary
|
||||||
|
public static final String RoomName = "room_name"; // Not in the spec, arbitrary
|
||||||
|
public static final String Sender = "sender";
|
||||||
|
public static final String SenderDisplayName = "sender_display_name"; // Not in the spec, arbitrary
|
||||||
|
public static final String SenderAvatarURL = "sender_avatar_url"; // Not in the spec, arbitrary
|
||||||
|
public static final String GuestAccessToken = "guest_access_token"; // Not in the spec, arbitrary
|
||||||
|
public static final String GuestUserID = "guest_user_id"; // Not in the spec, arbitrary
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@SerializedName(Keys.Medium)
|
||||||
|
private String medium;
|
||||||
|
|
||||||
|
@SerializedName(Keys.Address)
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
@SerializedName(Keys.RoomID)
|
||||||
|
private String roomId;
|
||||||
|
|
||||||
|
@SerializedName(Keys.RoomAlias)
|
||||||
|
private String roomAlias;
|
||||||
|
|
||||||
|
@SerializedName(Keys.RoomAvatarURL)
|
||||||
|
private String roomAvatarUrl;
|
||||||
|
|
||||||
|
@SerializedName(Keys.RoomJoinRules)
|
||||||
|
private String roomJoinRules;
|
||||||
|
|
||||||
|
@SerializedName(Keys.RoomName)
|
||||||
|
private String roomName;
|
||||||
|
|
||||||
|
@SerializedName(Keys.Sender)
|
||||||
|
private String sender;
|
||||||
|
|
||||||
|
@SerializedName(Keys.SenderDisplayName)
|
||||||
|
private String senderDisplayName;
|
||||||
|
|
||||||
|
@SerializedName(Keys.SenderAvatarURL)
|
||||||
|
private String senderAvatarUrl;
|
||||||
|
|
||||||
|
@SerializedName(Keys.GuestAccessToken)
|
||||||
|
private String guestAccessToken;
|
||||||
|
|
||||||
|
@SerializedName(Keys.GuestUserID)
|
||||||
|
private String guestUserId;
|
||||||
|
|
||||||
|
public String getMedium() {
|
||||||
|
return medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMedium(String medium) {
|
||||||
|
this.medium = medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAddress() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAddress(String address) {
|
||||||
|
this.address = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRoomId() {
|
||||||
|
return roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoomId(String roomId) {
|
||||||
|
this.roomId = roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRoomAlias() {
|
||||||
|
return roomAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoomAlias(String roomAlias) {
|
||||||
|
this.roomAlias = roomAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRoomAvatarUrl() {
|
||||||
|
return roomAvatarUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoomAvatarUrl(String roomAvatarUrl) {
|
||||||
|
this.roomAvatarUrl = roomAvatarUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRoomJoinRules() {
|
||||||
|
return roomJoinRules;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoomJoinRules(String roomJoinRules) {
|
||||||
|
this.roomJoinRules = roomJoinRules;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRoomName() {
|
||||||
|
return roomName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoomName(String roomName) {
|
||||||
|
this.roomName = roomName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSender() {
|
||||||
|
return sender;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSender(String sender) {
|
||||||
|
this.sender = sender;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSenderDisplayName() {
|
||||||
|
return senderDisplayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSenderDisplayName(String senderDisplayName) {
|
||||||
|
this.senderDisplayName = senderDisplayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSenderAvatarUrl() {
|
||||||
|
return senderAvatarUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSenderAvatarUrl(String senderAvatarUrl) {
|
||||||
|
this.senderAvatarUrl = senderAvatarUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGuestAccessToken() {
|
||||||
|
return guestAccessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGuestAccessToken(String guestAccessToken) {
|
||||||
|
this.guestAccessToken = guestAccessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGuestUserId() {
|
||||||
|
return guestUserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGuestUserId(String guestUserId) {
|
||||||
|
this.guestUserId = guestUserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -20,10 +20,16 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
import io.kamax.matrix.MatrixID;
|
import io.kamax.matrix.MatrixID;
|
||||||
|
import io.kamax.matrix._MatrixID;
|
||||||
import io.kamax.matrix.crypto.KeyManager;
|
import io.kamax.matrix.crypto.KeyManager;
|
||||||
|
import io.kamax.matrix.json.GsonUtil;
|
||||||
import io.kamax.mxisd.config.ServerConfig;
|
import io.kamax.mxisd.config.ServerConfig;
|
||||||
|
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.StoreInviteRequest;
|
||||||
import io.kamax.mxisd.http.io.identity.ThreePidInviteReplyIO;
|
import io.kamax.mxisd.http.io.identity.ThreePidInviteReplyIO;
|
||||||
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
|
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
|
||||||
import io.kamax.mxisd.invitation.IThreePidInvite;
|
import io.kamax.mxisd.invitation.IThreePidInvite;
|
||||||
@@ -31,11 +37,13 @@ import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
|||||||
import io.kamax.mxisd.invitation.InvitationManager;
|
import io.kamax.mxisd.invitation.InvitationManager;
|
||||||
import io.kamax.mxisd.invitation.ThreePidInvite;
|
import io.kamax.mxisd.invitation.ThreePidInvite;
|
||||||
import io.undertow.server.HttpServerExchange;
|
import io.undertow.server.HttpServerExchange;
|
||||||
|
import io.undertow.util.QueryParameterUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public class StoreInviteHandler extends BasicHttpHandler {
|
public class StoreInviteHandler extends BasicHttpHandler {
|
||||||
|
|
||||||
@@ -53,21 +61,39 @@ public class StoreInviteHandler extends BasicHttpHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleRequest(HttpServerExchange exchange) {
|
public void handleRequest(HttpServerExchange exchange) {
|
||||||
Map<String, String> parameters = new HashMap<>();
|
String reqContentType = getContentType(exchange).orElse("application/octet-stream");
|
||||||
|
JsonObject invJson = new JsonObject();
|
||||||
|
|
||||||
for (Map.Entry<String, Deque<String>> entry : exchange.getQueryParameters().entrySet()) {
|
if (StringUtils.startsWith(reqContentType, "application/json")) {
|
||||||
if (Objects.nonNull(entry.getValue().peekFirst())) {
|
invJson = parseJsonObject(exchange);
|
||||||
parameters.put(entry.getKey(), entry.getValue().peekFirst());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO test with missing parameters to see behaviour
|
// Backward compatibility for pre-r0.1.0 implementations
|
||||||
String sender = parameters.get("sender");
|
else if (StringUtils.startsWith(reqContentType, "application/x-www-form-urlencoded")) {
|
||||||
String medium = parameters.get("medium");
|
String body = getBodyUtf8(exchange);
|
||||||
String address = parameters.get("address");
|
Map<String, Deque<String>> parms = QueryParameterUtils.parseQueryString(body, StandardCharsets.UTF_8.name());
|
||||||
String roomId = parameters.get("room_id");
|
for (Map.Entry<String, Deque<String>> entry : parms.entrySet()) {
|
||||||
|
if (entry.getValue().size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
IThreePidInvite invite = new ThreePidInvite(MatrixID.asAcceptable(sender), medium, address, roomId, parameters);
|
if (entry.getValue().size() > 1) {
|
||||||
|
throw new BadRequestException("key " + entry.getKey() + " has more than one value");
|
||||||
|
}
|
||||||
|
|
||||||
|
invJson.addProperty(entry.getKey(), entry.getValue().peekFirst());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new BadRequestException("Unsupported Content-Type: " + reqContentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
Type parmType = new TypeToken<Map<String, String>>() {
|
||||||
|
}.getType();
|
||||||
|
Map<String, String> parameters = GsonUtil.get().fromJson(invJson, parmType);
|
||||||
|
StoreInviteRequest inv = GsonUtil.get().fromJson(invJson, StoreInviteRequest.class);
|
||||||
|
_MatrixID sender = MatrixID.asAcceptable(inv.getSender());
|
||||||
|
|
||||||
|
IThreePidInvite invite = new ThreePidInvite(sender, inv.getMedium(), inv.getAddress(), inv.getRoomId(), parameters);
|
||||||
IThreePidInviteReply reply = invMgr.storeInvite(invite);
|
IThreePidInviteReply reply = invMgr.storeInvite(invite);
|
||||||
|
|
||||||
respondJson(exchange, new ThreePidInviteReplyIO(reply, keyMgr.getPublicKeyBase64(keyMgr.getCurrentIndex()), cfg.getPublicUrl()));
|
respondJson(exchange, new ThreePidInviteReplyIO(reply, keyMgr.getPublicKeyBase64(keyMgr.getCurrentIndex()), cfg.getPublicUrl()));
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ import io.kamax.mxisd.threepid.session.IThreePidSession;
|
|||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.apache.commons.lang.WordUtils;
|
import org.apache.commons.lang.WordUtils;
|
||||||
|
|
||||||
|
import static io.kamax.mxisd.http.io.identity.StoreInviteRequest.Keys.RoomName;
|
||||||
|
import static io.kamax.mxisd.http.io.identity.StoreInviteRequest.Keys.SenderDisplayName;
|
||||||
|
|
||||||
public abstract class PlaceholderNotificationGenerator {
|
public abstract class PlaceholderNotificationGenerator {
|
||||||
|
|
||||||
private MatrixConfig mxCfg;
|
private MatrixConfig mxCfg;
|
||||||
@@ -51,9 +54,9 @@ public abstract class PlaceholderNotificationGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected String populateForInvite(IMatrixIdInvite invite, String input) {
|
protected String populateForInvite(IMatrixIdInvite invite, String input) {
|
||||||
String senderName = invite.getProperties().getOrDefault("sender_display_name", "");
|
String senderName = invite.getProperties().getOrDefault(SenderDisplayName, "");
|
||||||
String senderNameOrId = StringUtils.defaultIfBlank(senderName, invite.getSender().getId());
|
String senderNameOrId = StringUtils.defaultIfBlank(senderName, invite.getSender().getId());
|
||||||
String roomName = invite.getProperties().getOrDefault("room_name", "");
|
String roomName = invite.getProperties().getOrDefault(RoomName, "");
|
||||||
String roomNameOrId = StringUtils.defaultIfBlank(roomName, invite.getRoomId());
|
String roomNameOrId = StringUtils.defaultIfBlank(roomName, invite.getRoomId());
|
||||||
|
|
||||||
return populateForCommon(new ThreePid(invite.getMedium(), invite.getAddress()), input)
|
return populateForCommon(new ThreePid(invite.getMedium(), invite.getAddress()), input)
|
||||||
@@ -69,9 +72,9 @@ public abstract class PlaceholderNotificationGenerator {
|
|||||||
protected String populateForReply(IThreePidInviteReply invite, String input) {
|
protected String populateForReply(IThreePidInviteReply invite, String input) {
|
||||||
ThreePid tpid = new ThreePid(invite.getInvite().getMedium(), invite.getInvite().getAddress());
|
ThreePid tpid = new ThreePid(invite.getInvite().getMedium(), invite.getInvite().getAddress());
|
||||||
|
|
||||||
String senderName = invite.getInvite().getProperties().getOrDefault("sender_display_name", "");
|
String senderName = invite.getInvite().getProperties().getOrDefault(SenderDisplayName, "");
|
||||||
String senderNameOrId = StringUtils.defaultIfBlank(senderName, invite.getInvite().getSender().getId());
|
String senderNameOrId = StringUtils.defaultIfBlank(senderName, invite.getInvite().getSender().getId());
|
||||||
String roomName = invite.getInvite().getProperties().getOrDefault("room_name", "");
|
String roomName = invite.getInvite().getProperties().getOrDefault(RoomName, "");
|
||||||
String roomNameOrId = StringUtils.defaultIfBlank(roomName, invite.getInvite().getRoomId());
|
String roomNameOrId = StringUtils.defaultIfBlank(roomName, invite.getInvite().getRoomId());
|
||||||
|
|
||||||
return populateForCommon(tpid, input)
|
return populateForCommon(tpid, input)
|
||||||
|
|||||||
Reference in New Issue
Block a user