diff --git a/src/main/groovy/io/kamax/mxisd/auth/AuthManager.java b/src/main/groovy/io/kamax/mxisd/auth/AuthManager.java index 61f33e7..6dbfff2 100644 --- a/src/main/groovy/io/kamax/mxisd/auth/AuthManager.java +++ b/src/main/groovy/io/kamax/mxisd/auth/AuthManager.java @@ -57,6 +57,8 @@ public class AuthManager { invMgr.publishMappingIfInvited(new ThreePidMapping(pid, result.getMxid())); } + invMgr.lookupMappingsForInvites(); + return result; } } diff --git a/src/main/groovy/io/kamax/mxisd/controller/v1/MappingController.groovy b/src/main/groovy/io/kamax/mxisd/controller/v1/MappingController.groovy index 050914b..fdd9ef3 100644 --- a/src/main/groovy/io/kamax/mxisd/controller/v1/MappingController.groovy +++ b/src/main/groovy/io/kamax/mxisd/controller/v1/MappingController.groovy @@ -20,18 +20,20 @@ package io.kamax.mxisd.controller.v1 +import com.google.gson.Gson +import com.google.gson.JsonObject import groovy.json.JsonOutput import groovy.json.JsonSlurper -import io.kamax.mxisd.lookup.ALookupRequest -import io.kamax.mxisd.lookup.BulkLookupRequest -import io.kamax.mxisd.lookup.SingleLookupRequest -import io.kamax.mxisd.lookup.ThreePidMapping +import io.kamax.mxisd.controller.v1.io.SingeLookupReplyJson +import io.kamax.mxisd.lookup.* import io.kamax.mxisd.lookup.strategy.LookupStrategy import io.kamax.mxisd.signature.SignatureManager import org.apache.commons.lang.StringUtils import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired +import org.springframework.http.MediaType +import org.springframework.web.bind.annotation.CrossOrigin import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController @@ -42,10 +44,13 @@ import static org.springframework.web.bind.annotation.RequestMethod.GET import static org.springframework.web.bind.annotation.RequestMethod.POST @RestController +@CrossOrigin +@RequestMapping(path = "/_matrix/identity/api/v1", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) class MappingController { private Logger log = LoggerFactory.getLogger(MappingController.class) private JsonSlurper json = new JsonSlurper() + private Gson gson = new Gson() @Autowired private LookupStrategy strategy @@ -62,7 +67,7 @@ class MappingController { } } - @RequestMapping(value = "/_matrix/identity/api/v1/lookup", method = GET) + @RequestMapping(value = "/lookup", method = GET) String lookup(HttpServletRequest request, @RequestParam String medium, @RequestParam String address) { SingleLookupRequest lookupRequest = new SingleLookupRequest() setRequesterInfo(lookupRequest, request) @@ -71,22 +76,26 @@ class MappingController { log.info("Got request from {} - Is recursive? {}", lookupRequest.getRequester(), lookupRequest.isRecursive()) - Optional lookupOpt = strategy.find(lookupRequest) + Optional lookupOpt = strategy.find(lookupRequest) if (!lookupOpt.isPresent()) { log.info("No mapping was found, return empty JSON object") return JsonOutput.toJson([]) } - def lookup = lookupOpt.get() - if (lookup['signatures'] == null) { - log.info("lookup is not signed yet, we sign it") - lookup['signatures'] = signMgr.signMessage(JsonOutput.toJson(lookup)) - } + SingleLookupReply lookup = lookupOpt.get() + if (lookup.isSigned()) { + log.info("Lookup is already signed, sending as-is") + return lookup.getBody(); + } else { + log.info("Lookup is not signed, signing") + JsonObject obj = new Gson().toJsonTree(new SingeLookupReplyJson(lookup)).getAsJsonObject() + obj.add("signatures", signMgr.signMessageGson(gson.toJson(obj))) - return JsonOutput.toJson(lookup) + return gson.toJson(obj) + } } - @RequestMapping(value = "/_matrix/identity/api/v1/bulk_lookup", method = POST) + @RequestMapping(value = "/bulk_lookup", method = POST) String bulkLookup(HttpServletRequest request) { BulkLookupRequest lookupRequest = new BulkLookupRequest() setRequesterInfo(lookupRequest, request) diff --git a/src/main/groovy/io/kamax/mxisd/controller/v1/io/SingeLookupReplyJson.java b/src/main/groovy/io/kamax/mxisd/controller/v1/io/SingeLookupReplyJson.java new file mode 100644 index 0000000..5627664 --- /dev/null +++ b/src/main/groovy/io/kamax/mxisd/controller/v1/io/SingeLookupReplyJson.java @@ -0,0 +1,75 @@ +/* + * mxisd - Matrix Identity Server Daemon + * Copyright (C) 2017 Maxime Dor + * + * https://max.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.controller.v1.io; + +import io.kamax.mxisd.lookup.SingleLookupReply; + +import java.util.HashMap; +import java.util.Map; + +public class SingeLookupReplyJson { + + private String address; + private String medium; + private String mxid; + private long not_after; + private long not_before; + private long ts; + private Map> signatures = new HashMap<>(); + + public SingeLookupReplyJson(SingleLookupReply reply) { + this.address = reply.getRequest().getThreePid(); + this.medium = reply.getRequest().getType(); + this.mxid = reply.getMxid().getId(); + this.not_after = reply.getNotAfter().toEpochMilli(); + this.not_before = reply.getNotBefore().toEpochMilli(); + this.ts = reply.getTimestamp().toEpochMilli(); + } + + public String getAddress() { + return address; + } + + public String getMedium() { + return medium; + } + + public String getMxid() { + return mxid; + } + + public long getNot_after() { + return not_after; + } + + public long getNot_before() { + return not_before; + } + + public long getTs() { + return ts; + } + + public boolean isSigned() { + return signatures != null && !signatures.isEmpty(); + } + +} diff --git a/src/main/groovy/io/kamax/mxisd/invitation/InvitationManager.java b/src/main/groovy/io/kamax/mxisd/invitation/InvitationManager.java index 5d7510c..bdb2266 100644 --- a/src/main/groovy/io/kamax/mxisd/invitation/InvitationManager.java +++ b/src/main/groovy/io/kamax/mxisd/invitation/InvitationManager.java @@ -25,6 +25,7 @@ import io.kamax.matrix.ThreePid; import io.kamax.mxisd.exception.BadRequestException; import io.kamax.mxisd.exception.MappingAlreadyExistsException; import io.kamax.mxisd.invitation.sender.IInviteSender; +import io.kamax.mxisd.lookup.SingleLookupReply; import io.kamax.mxisd.lookup.ThreePidMapping; import io.kamax.mxisd.lookup.strategy.LookupStrategy; import io.kamax.mxisd.signature.SignatureManager; @@ -51,12 +52,15 @@ import org.xbill.DNS.TextParseException; import org.xbill.DNS.Type; import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.TimeUnit; @Component public class InvitationManager { @@ -92,6 +96,15 @@ public class InvitationManager { } } + @PreDestroy + private void preDestroy() { + ForkJoinPool.commonPool().awaitQuiescence(1, TimeUnit.MINUTES); + } + + private String getId(IThreePidInviteReply reply) { + return reply.getInvite().getSender().getId() + ":" + reply.getInvite().getRoomId() + ":" + reply.getInvite().getMedium() + ":" + reply.getInvite().getAddress(); + } + String getSrvRecordName(String domain) { return "_matrix._tcp." + domain; } @@ -162,15 +175,28 @@ public class InvitationManager { return reply; } + public void lookupMappingsForInvites() { + log.info("Checking for existing mapping for pending invites"); + for (IThreePidInviteReply reply : invitations.values()) { + log.info("Processing invite {}", getId(reply)); + ForkJoinPool.commonPool().submit(new MappingChecker(reply)); + } + } + public void publishMappingIfInvited(ThreePidMapping threePid) { ThreePid key = new ThreePid(threePid.getMedium(), threePid.getValue()); IThreePidInviteReply reply = invitations.get(key); if (reply == null) { log.info("{}:{} does not have a pending invite, no mapping to publish", threePid.getMedium(), threePid.getValue()); - return; + } else { + log.info("{}:{} has an invite pending, publishing mapping", threePid.getMedium(), threePid.getValue()); + publishMapping(reply, threePid.getMxid()); } + } - log.info("{}:{} has an invite pending, publishing mapping", threePid.getMedium(), threePid.getValue()); + private void publishMapping(IThreePidInviteReply reply, String mxid) { + String medium = reply.getInvite().getMedium(); + String address = reply.getInvite().getAddress(); String domain = reply.getInvite().getSender().getDomain(); log.info("Discovering HS for domain {}", domain); String hsUrlOpt = findHomeserverForDomain(domain); @@ -180,14 +206,14 @@ public class InvitationManager { HttpPost req = new HttpPost(hsUrlOpt + "/_matrix/federation/v1/3pid/onbind"); // Expected body: https://matrix.to/#/!HUeDbmFUsWAhxHHvFG:matrix.org/$150469846739DCLWc:matrix.trancendances.fr JSONObject obj = new JSONObject(); // TODO use Gson instead - obj.put("mxid", threePid.getMxid()); + obj.put("mxid", mxid); obj.put("token", reply.getToken()); obj.put("signatures", signMgr.signMessageJson(obj.toString())); JSONObject objUp = new JSONObject(); - objUp.put("mxid", threePid.getMxid()); - objUp.put("medium", threePid.getMedium()); - objUp.put("address", threePid.getValue()); + objUp.put("mxid", mxid); + objUp.put("medium", medium); + objUp.put("address", address); objUp.put("sender", reply.getInvite().getSender().getId()); objUp.put("room_id", reply.getInvite().getRoomId()); objUp.put("signed", obj); @@ -196,9 +222,9 @@ public class InvitationManager { JSONArray invites = new JSONArray(); invites.put(objUp); content.put("invites", invites); - content.put("medium", threePid.getMedium()); - content.put("address", threePid.getValue()); - content.put("mxid", threePid.getMxid()); + content.put("medium", medium); + content.put("address", address); + content.put("mxid", mxid); content.put("signatures", signMgr.signMessageJson(content.toString())); @@ -217,8 +243,36 @@ public class InvitationManager { } catch (IOException e) { log.warn("Unable to tell HS {} about invite being mapped", domain, e); } - invitations.remove(key); + invitations.remove(new ThreePid(medium, address)); + log.info("Removed invite from internal store"); }).start(); } + private class MappingChecker implements Runnable { + + private IThreePidInviteReply reply; + + public MappingChecker(IThreePidInviteReply reply) { + this.reply = reply; + } + + @Override + public void run() { + try { + log.info("Searching for mapping created since invite {} was created", getId(reply)); + Optional result = lookupMgr.find(reply.getInvite().getMedium(), reply.getInvite().getAddress(), true); + log.info("Result: {}", result); + if (result.isPresent()) { + SingleLookupReply lookup = result.get(); + log.info("Found mapping for pending invite {}", getId(reply)); + publishMapping(reply, lookup.getMxid().getId()); + } else { + log.info("No mapping for pending invite {}", getId(reply)); + } + } catch (Throwable t) { + log.error("Unable to process invite", t); + } + } + } + } diff --git a/src/main/groovy/io/kamax/mxisd/lookup/SingleLookupReply.java b/src/main/groovy/io/kamax/mxisd/lookup/SingleLookupReply.java new file mode 100644 index 0000000..d3a212c --- /dev/null +++ b/src/main/groovy/io/kamax/mxisd/lookup/SingleLookupReply.java @@ -0,0 +1,116 @@ +/* + * mxisd - Matrix Identity Server Daemon + * Copyright (C) 2017 Maxime Dor + * + * https://max.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.lookup; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import io.kamax.matrix.MatrixID; +import io.kamax.matrix._MatrixID; +import io.kamax.mxisd.controller.v1.io.SingeLookupReplyJson; + +import java.time.Instant; + +public class SingleLookupReply { + + private static Gson gson = new Gson(); + + private boolean isRecursive; + private boolean isSigned; + private String body; + private SingleLookupRequest request; + private _MatrixID mxid; + private Instant notBefore; + private Instant notAfter; + private Instant timestamp; + + public static SingleLookupReply fromRecursive(SingleLookupRequest request, String body) { + SingleLookupReply reply = new SingleLookupReply(); + reply.isRecursive = true; + reply.request = request; + reply.body = body; + + try { + SingeLookupReplyJson json = gson.fromJson(body, SingeLookupReplyJson.class); + reply.mxid = new MatrixID(json.getMxid()); + reply.notAfter = Instant.ofEpochMilli(json.getNot_after()); + reply.notBefore = Instant.ofEpochMilli(json.getNot_before()); + reply.timestamp = Instant.ofEpochMilli(json.getTs()); + reply.isSigned = json.isSigned(); + } catch (JsonSyntaxException e) { + // stub - we only want to try, nothing more + } + + return reply; + } + + private SingleLookupReply() { + // stub + } + + public SingleLookupReply(SingleLookupRequest request, String mxid) { + this(request, new MatrixID(mxid)); + } + + public SingleLookupReply(SingleLookupRequest request, _MatrixID mxid) { + this(request, mxid, Instant.now(), Instant.ofEpochMilli(0), Instant.ofEpochMilli(253402300799000L)); + } + + public SingleLookupReply(SingleLookupRequest request, _MatrixID mxid, Instant timestamp, Instant notBefore, Instant notAfter) { + this.request = request; + this.mxid = mxid; + this.timestamp = timestamp; + this.notBefore = notBefore; + this.notAfter = notAfter; + } + + public boolean isRecursive() { + return isRecursive; + } + + public boolean isSigned() { + return isSigned; + } + + public String getBody() { + return body; + } + + public SingleLookupRequest getRequest() { + return request; + } + + public _MatrixID getMxid() { + return mxid; + } + + public Instant getNotBefore() { + return notBefore; + } + + public Instant getNotAfter() { + return notAfter; + } + + public Instant getTimestamp() { + return timestamp; + } + +} diff --git a/src/main/groovy/io/kamax/mxisd/lookup/fetcher/IBridgeFetcher.groovy b/src/main/groovy/io/kamax/mxisd/lookup/fetcher/IBridgeFetcher.groovy index a3d3624..bd445bb 100644 --- a/src/main/groovy/io/kamax/mxisd/lookup/fetcher/IBridgeFetcher.groovy +++ b/src/main/groovy/io/kamax/mxisd/lookup/fetcher/IBridgeFetcher.groovy @@ -20,12 +20,13 @@ package io.kamax.mxisd.lookup.fetcher +import io.kamax.mxisd.lookup.SingleLookupReply import io.kamax.mxisd.lookup.SingleLookupRequest import io.kamax.mxisd.lookup.ThreePidMapping interface IBridgeFetcher { - Optional find(SingleLookupRequest request) + Optional find(SingleLookupRequest request) List populate(List mappings) diff --git a/src/main/groovy/io/kamax/mxisd/lookup/fetcher/IRemoteIdentityServerFetcher.groovy b/src/main/groovy/io/kamax/mxisd/lookup/fetcher/IRemoteIdentityServerFetcher.groovy index 2b45959..aeb81d5 100644 --- a/src/main/groovy/io/kamax/mxisd/lookup/fetcher/IRemoteIdentityServerFetcher.groovy +++ b/src/main/groovy/io/kamax/mxisd/lookup/fetcher/IRemoteIdentityServerFetcher.groovy @@ -20,13 +20,15 @@ package io.kamax.mxisd.lookup.fetcher +import io.kamax.mxisd.lookup.SingleLookupReply +import io.kamax.mxisd.lookup.SingleLookupRequest import io.kamax.mxisd.lookup.ThreePidMapping interface IRemoteIdentityServerFetcher { boolean isUsable(String remote) - Optional find(String remote, String type, String threePid) + Optional find(String remote, SingleLookupRequest request) List find(String remote, List mappings) diff --git a/src/main/groovy/io/kamax/mxisd/lookup/provider/BridgeFetcher.java b/src/main/groovy/io/kamax/mxisd/lookup/provider/BridgeFetcher.java index ec1c8cd..bb8a85f 100644 --- a/src/main/groovy/io/kamax/mxisd/lookup/provider/BridgeFetcher.java +++ b/src/main/groovy/io/kamax/mxisd/lookup/provider/BridgeFetcher.java @@ -21,6 +21,7 @@ package io.kamax.mxisd.lookup.provider; import io.kamax.mxisd.config.RecursiveLookupBridgeConfig; +import io.kamax.mxisd.lookup.SingleLookupReply; import io.kamax.mxisd.lookup.SingleLookupRequest; import io.kamax.mxisd.lookup.ThreePidMapping; import io.kamax.mxisd.lookup.fetcher.IBridgeFetcher; @@ -46,16 +47,16 @@ public class BridgeFetcher implements IBridgeFetcher { private RemoteIdentityServerFetcher fetcher; @Override - public Optional find(SingleLookupRequest request) { + public Optional find(SingleLookupRequest request) { Optional mediumUrl = Optional.ofNullable(cfg.getMappings().get(request.getType())); if (mediumUrl.isPresent() && !StringUtils.isBlank(mediumUrl.get())) { log.info("Using specific medium bridge lookup URL {}", mediumUrl.get()); - return fetcher.find(mediumUrl.get(), request.getType(), request.getThreePid()); + return fetcher.find(mediumUrl.get(), request); } else if (!StringUtils.isBlank(cfg.getServer())) { log.info("Using generic bridge lookup URL {}", cfg.getServer()); - return fetcher.find(cfg.getServer(), request.getType(), request.getThreePid()); + return fetcher.find(cfg.getServer(), request); } else { log.info("No bridge lookup URL found/configured, skipping"); diff --git a/src/main/groovy/io/kamax/mxisd/lookup/provider/DnsLookupProvider.groovy b/src/main/groovy/io/kamax/mxisd/lookup/provider/DnsLookupProvider.groovy index a7076a1..40aec25 100644 --- a/src/main/groovy/io/kamax/mxisd/lookup/provider/DnsLookupProvider.groovy +++ b/src/main/groovy/io/kamax/mxisd/lookup/provider/DnsLookupProvider.groovy @@ -21,6 +21,7 @@ package io.kamax.mxisd.lookup.provider import io.kamax.mxisd.config.ServerConfig +import io.kamax.mxisd.lookup.SingleLookupReply import io.kamax.mxisd.lookup.SingleLookupRequest import io.kamax.mxisd.lookup.ThreePidMapping import io.kamax.mxisd.lookup.fetcher.IRemoteIdentityServerFetcher @@ -124,7 +125,7 @@ class DnsLookupProvider implements IThreePidProvider { } @Override - Optional find(SingleLookupRequest request) { + Optional find(SingleLookupRequest request) { if (!StringUtils.equals("email", request.getType())) { // TODO use enum log.info("Skipping unsupported type {} for {}", request.getType(), request.getThreePid()) return Optional.empty() @@ -137,7 +138,7 @@ class DnsLookupProvider implements IThreePidProvider { Optional baseUrl = findIdentityServerForDomain(domain) if (baseUrl.isPresent()) { - return fetcher.find(baseUrl.get(), request.getType().toString(), request.getThreePid()) + return fetcher.find(baseUrl.get(), request) } return Optional.empty() diff --git a/src/main/groovy/io/kamax/mxisd/lookup/provider/ForwarderProvider.groovy b/src/main/groovy/io/kamax/mxisd/lookup/provider/ForwarderProvider.groovy index f2253dc..1ef903a 100644 --- a/src/main/groovy/io/kamax/mxisd/lookup/provider/ForwarderProvider.groovy +++ b/src/main/groovy/io/kamax/mxisd/lookup/provider/ForwarderProvider.groovy @@ -21,6 +21,7 @@ package io.kamax.mxisd.lookup.provider import io.kamax.mxisd.config.ForwardConfig +import io.kamax.mxisd.lookup.SingleLookupReply import io.kamax.mxisd.lookup.SingleLookupRequest import io.kamax.mxisd.lookup.ThreePidMapping import io.kamax.mxisd.lookup.fetcher.IRemoteIdentityServerFetcher @@ -56,9 +57,9 @@ class ForwarderProvider implements IThreePidProvider { } @Override - Optional find(SingleLookupRequest request) { + Optional find(SingleLookupRequest request) { for (String root : cfg.getServers()) { - Optional answer = fetcher.find(root, request.getType(), request.getThreePid()) + Optional answer = fetcher.find(root, request) if (answer.isPresent()) { return answer } diff --git a/src/main/groovy/io/kamax/mxisd/lookup/provider/GoogleFirebaseProvider.groovy b/src/main/groovy/io/kamax/mxisd/lookup/provider/GoogleFirebaseProvider.groovy index 077974b..30d3c7b 100644 --- a/src/main/groovy/io/kamax/mxisd/lookup/provider/GoogleFirebaseProvider.groovy +++ b/src/main/groovy/io/kamax/mxisd/lookup/provider/GoogleFirebaseProvider.groovy @@ -30,6 +30,7 @@ import com.google.firebase.internal.NonNull import com.google.firebase.tasks.OnFailureListener import com.google.firebase.tasks.OnSuccessListener import io.kamax.matrix.ThreePidMedium +import io.kamax.mxisd.lookup.SingleLookupReply import io.kamax.mxisd.lookup.SingleLookupRequest import io.kamax.mxisd.lookup.ThreePidMapping import org.apache.commons.lang.StringUtils @@ -88,6 +89,10 @@ public class GoogleFirebaseProvider implements IThreePidProvider { .build(); } + private String getMxid(UserRecord record) { + return "@${record.getUid()}:${domain}"; + } + @Override public boolean isEnabled() { return isEnabled; @@ -154,20 +159,13 @@ public class GoogleFirebaseProvider implements IThreePidProvider { } @Override - public Optional find(SingleLookupRequest request) { + public Optional find(SingleLookupRequest request) { Optional urOpt = findInternal(request.getType(), request.getThreePid()) if (urOpt.isPresent()) { - return [ - address : request.getThreePid(), - medium : request.getType(), - mxid : "@${urOpt.get().getUid()}:${domain}", - not_before: 0, - not_after : 9223372036854775807, - ts : 0 - ] - } else { - return Optional.empty(); + return Optional.of(new SingleLookupReply(request, getMxid(urOpt.get()))); } + + return Optional.empty(); } @Override @@ -181,7 +179,7 @@ public class GoogleFirebaseProvider implements IThreePidProvider { ThreePidMapping result = new ThreePidMapping(); result.setMedium(o.getMedium()) result.setValue(o.getValue()) - result.setMxid("@${urOpt.get().getUid()}:${domain}") + result.setMxid(getMxid(urOpt.get())) results.add(result) } } diff --git a/src/main/groovy/io/kamax/mxisd/lookup/provider/IThreePidProvider.groovy b/src/main/groovy/io/kamax/mxisd/lookup/provider/IThreePidProvider.groovy index 445afda..614cc04 100644 --- a/src/main/groovy/io/kamax/mxisd/lookup/provider/IThreePidProvider.groovy +++ b/src/main/groovy/io/kamax/mxisd/lookup/provider/IThreePidProvider.groovy @@ -20,6 +20,7 @@ package io.kamax.mxisd.lookup.provider +import io.kamax.mxisd.lookup.SingleLookupReply import io.kamax.mxisd.lookup.SingleLookupRequest import io.kamax.mxisd.lookup.ThreePidMapping @@ -34,7 +35,7 @@ interface IThreePidProvider { */ int getPriority() // Should not be here but let's KISS for now - Optional find(SingleLookupRequest request) + Optional find(SingleLookupRequest request) List populate(List mappings) diff --git a/src/main/groovy/io/kamax/mxisd/lookup/provider/LdapProvider.groovy b/src/main/groovy/io/kamax/mxisd/lookup/provider/LdapProvider.groovy index 1984f32..c2cc220 100644 --- a/src/main/groovy/io/kamax/mxisd/lookup/provider/LdapProvider.groovy +++ b/src/main/groovy/io/kamax/mxisd/lookup/provider/LdapProvider.groovy @@ -22,6 +22,7 @@ package io.kamax.mxisd.lookup.provider import io.kamax.mxisd.config.ServerConfig import io.kamax.mxisd.config.ldap.LdapConfig +import io.kamax.mxisd.lookup.SingleLookupReply import io.kamax.mxisd.lookup.SingleLookupRequest import io.kamax.mxisd.lookup.ThreePidMapping import org.apache.commons.lang.StringUtils @@ -131,7 +132,7 @@ class LdapProvider implements IThreePidProvider { } @Override - Optional find(SingleLookupRequest request) { + Optional find(SingleLookupRequest request) { log.info("Performing LDAP lookup ${request.getThreePid()} of type ${request.getType()}") LdapConnection conn = getConn() @@ -140,14 +141,7 @@ class LdapProvider implements IThreePidProvider { Optional mxid = lookup(conn, request.getType(), request.getThreePid()) if (mxid.isPresent()) { - return Optional.of([ - address : request.getThreePid(), - medium : request.getType(), - mxid : mxid.get(), - not_before: 0, - not_after : 9223372036854775807, - ts : 0 - ]) + return Optional.of(new SingleLookupReply(request, mxid.get())); } } finally { conn.close() diff --git a/src/main/groovy/io/kamax/mxisd/lookup/provider/RemoteIdentityServerFetcher.groovy b/src/main/groovy/io/kamax/mxisd/lookup/provider/RemoteIdentityServerFetcher.groovy index 36a38de..7435879 100644 --- a/src/main/groovy/io/kamax/mxisd/lookup/provider/RemoteIdentityServerFetcher.groovy +++ b/src/main/groovy/io/kamax/mxisd/lookup/provider/RemoteIdentityServerFetcher.groovy @@ -24,6 +24,8 @@ import groovy.json.JsonException import groovy.json.JsonOutput import groovy.json.JsonSlurper import io.kamax.mxisd.controller.v1.ClientBulkLookupRequest +import io.kamax.mxisd.lookup.SingleLookupReply +import io.kamax.mxisd.lookup.SingleLookupRequest import io.kamax.mxisd.lookup.ThreePidMapping import io.kamax.mxisd.lookup.fetcher.IRemoteIdentityServerFetcher import org.apache.http.HttpEntity @@ -77,24 +79,26 @@ public class RemoteIdentityServerFetcher implements IRemoteIdentityServerFetcher } @Override - Optional find(String remote, String type, String threePid) { - log.info("Looking up {} 3PID {} using {}", type, threePid, remote) + Optional find(String remote, SingleLookupRequest request) { + log.info("Looking up {} 3PID {} using {}", request.getType(), request.getThreePid(), remote) HttpURLConnection rootSrvConn = (HttpURLConnection) new URL( - "${remote}/_matrix/identity/api/v1/lookup?medium=${type}&address=${threePid}" + "${remote}/_matrix/identity/api/v1/lookup?medium=${request.getType()}&address=${request.getThreePid()}" ).openConnection() try { - def output = json.parseText(rootSrvConn.getInputStream().getText()) + String outputRaw = rootSrvConn.getInputStream().getText() + def output = json.parseText(outputRaw) if (output['address']) { log.info("Found 3PID mapping: {}", output) - return Optional.of(output) + + return Optional.of(SingleLookupReply.fromRecursive(request, outputRaw)) } log.info("Empty 3PID mapping from {}", remote) return Optional.empty() } catch (IOException e) { - log.warn("Error looking up 3PID mapping {}: {}", threePid, e.getMessage()) + log.warn("Error looking up 3PID mapping {}: {}", request.getThreePid(), e.getMessage()) return Optional.empty() } catch (JsonException e) { log.warn("Invalid JSON answer from {}", remote) diff --git a/src/main/groovy/io/kamax/mxisd/lookup/strategy/LookupStrategy.groovy b/src/main/groovy/io/kamax/mxisd/lookup/strategy/LookupStrategy.groovy index a4fd5e7..37e4327 100644 --- a/src/main/groovy/io/kamax/mxisd/lookup/strategy/LookupStrategy.groovy +++ b/src/main/groovy/io/kamax/mxisd/lookup/strategy/LookupStrategy.groovy @@ -21,6 +21,7 @@ package io.kamax.mxisd.lookup.strategy import io.kamax.mxisd.lookup.BulkLookupRequest +import io.kamax.mxisd.lookup.SingleLookupReply import io.kamax.mxisd.lookup.SingleLookupRequest import io.kamax.mxisd.lookup.ThreePidMapping import io.kamax.mxisd.lookup.provider.IThreePidProvider @@ -29,11 +30,11 @@ interface LookupStrategy { List getLocalProviders() - Optional find(String medium, String address, boolean recursive) + Optional find(String medium, String address, boolean recursive) - Optional find(SingleLookupRequest request) + Optional find(SingleLookupRequest request) - Optional findRecursive(SingleLookupRequest request) + Optional findRecursive(SingleLookupRequest request) List find(BulkLookupRequest requests) diff --git a/src/main/groovy/io/kamax/mxisd/lookup/strategy/RecursivePriorityLookupStrategy.groovy b/src/main/groovy/io/kamax/mxisd/lookup/strategy/RecursivePriorityLookupStrategy.groovy index 55eedb0..e4ef776 100644 --- a/src/main/groovy/io/kamax/mxisd/lookup/strategy/RecursivePriorityLookupStrategy.groovy +++ b/src/main/groovy/io/kamax/mxisd/lookup/strategy/RecursivePriorityLookupStrategy.groovy @@ -22,10 +22,7 @@ package io.kamax.mxisd.lookup.strategy import edazdarevic.commons.net.CIDRUtils import io.kamax.mxisd.config.RecursiveLookupConfig -import io.kamax.mxisd.lookup.ALookupRequest -import io.kamax.mxisd.lookup.BulkLookupRequest -import io.kamax.mxisd.lookup.SingleLookupRequest -import io.kamax.mxisd.lookup.ThreePidMapping +import io.kamax.mxisd.lookup.* import io.kamax.mxisd.lookup.fetcher.IBridgeFetcher import io.kamax.mxisd.lookup.provider.IThreePidProvider import org.slf4j.Logger @@ -122,7 +119,7 @@ class RecursivePriorityLookupStrategy implements LookupStrategy, InitializingBea } @Override - Optional find(String medium, String address, boolean recursive) { + Optional find(String medium, String address, boolean recursive) { SingleLookupRequest req = new SingleLookupRequest(); req.setType(medium) req.setThreePid(address) @@ -130,9 +127,9 @@ class RecursivePriorityLookupStrategy implements LookupStrategy, InitializingBea return find(req, recursive) } - Optional find(SingleLookupRequest request, boolean forceRecursive) { + Optional find(SingleLookupRequest request, boolean forceRecursive) { for (IThreePidProvider provider : listUsableProviders(request, forceRecursive)) { - Optional lookupDataOpt = provider.find(request) + Optional lookupDataOpt = provider.find(request) if (lookupDataOpt.isPresent()) { return lookupDataOpt } @@ -151,12 +148,12 @@ class RecursivePriorityLookupStrategy implements LookupStrategy, InitializingBea } @Override - Optional find(SingleLookupRequest request) { + Optional find(SingleLookupRequest request) { return find(request, false) } @Override - Optional findRecursive(SingleLookupRequest request) { + Optional findRecursive(SingleLookupRequest request) { return find(request, true) } diff --git a/src/main/groovy/io/kamax/mxisd/signature/SignatureManager.groovy b/src/main/groovy/io/kamax/mxisd/signature/SignatureManager.groovy index 72dede2..84c4ad9 100644 --- a/src/main/groovy/io/kamax/mxisd/signature/SignatureManager.groovy +++ b/src/main/groovy/io/kamax/mxisd/signature/SignatureManager.groovy @@ -20,6 +20,7 @@ package io.kamax.mxisd.signature +import com.google.gson.JsonObject import io.kamax.mxisd.config.ServerConfig import io.kamax.mxisd.key.KeyManager import net.i2p.crypto.eddsa.EdDSAEngine @@ -41,20 +42,13 @@ class SignatureManager implements InitializingBean { private EdDSAEngine signEngine - // FIXME we need to return a proper object, or a string with the signature and all? - Map signMessage(String message) { + private String sign(String message) { byte[] signRaw = signEngine.signOneShot(message.getBytes()) - String sign = Base64.getEncoder().encodeToString(signRaw) - return [ - "${srvCfg.getName()}": [ - "ed25519:${keyMgr.getCurrentIndex()}": sign - ] - ] + return Base64.getEncoder().encodeToString(signRaw) } JSONObject signMessageJson(String message) { - byte[] signRaw = signEngine.signOneShot(message.getBytes()) - String sign = Base64.getEncoder().encodeToString(signRaw) + String sign = sign(message) JSONObject keySignature = new JSONObject() keySignature.put("ed25519:${keyMgr.getCurrentIndex()}", sign) @@ -64,6 +58,17 @@ class SignatureManager implements InitializingBean { return signature } + JsonObject signMessageGson(String message) { + String sign = sign(message) + + JsonObject keySignature = new JsonObject() + keySignature.addProperty("ed25519:${keyMgr.getCurrentIndex()}", sign) + JsonObject signature = new JsonObject() + signature.add("${srvCfg.getName()}", keySignature); + + return signature + } + @Override void afterPropertiesSet() throws Exception { signEngine = new EdDSAEngine(MessageDigest.getInstance(keyMgr.getSpecs().getHashAlgorithm()))