diff --git a/README.md b/README.md index e474c2d..ab31e8a 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,13 @@ mxisd is an implementation of the Matrix Identity Server which aims to provide a to [sydent](https://github.com/matrix-org/sydent) and an external validation implementation of the [Identity Service API](http://matrix.org/docs/spec/identity_service/unstable.html). -mxisd is currently in read-only mode with the following lookup strategies in priority order: +mxisd is currently in read-only mode and use a priority lookup strategy with several providers. + +Given the 3PID `john.doe@example.org`, the following would be performed in priority order until a mapping is found: - LDAP: lookup the Matrix ID from an configurable attribute. +- DNS: lookup another Identity Server using the domain part of an e-mail and: + - Look for a SRV record under `_identity._matrix._tcp.example.org` + - Lookup using the base domain name `example.org` - Forwarder: Proxy the request to other identity servers (`matrix.org` and `vector.im` currently hardcoded). # Quick start @@ -23,7 +28,7 @@ cd mxisd ## Configure 1. Create a new local config: `cp application.example.yaml application.yaml` - Edit `application.yaml` to your needs - at least provide the LDAP attributes -- Edit an entity in your LDAP database and set the configure attribute with a Matrix ID (e.g. @johndoe:example.org) +- Edit an entity in your LDAP database and set the configure attribute with a Matrix ID (e.g. `@john.doe:example.org`) ## Run Start the server in foreground: @@ -38,7 +43,7 @@ curl http://localhost:8090/_matrix/identity/api/v1/pubkey/ed25519:0 Validate your LDAP config and binding info (replace the e-mail): ``` -curl http://localhost:8090/_matrix/identity/api/v1/lookup?medium=email&address=johndoe@example.org +curl "http://localhost:8090/_matrix/identity/api/v1/lookup?medium=email&address=john.doe@example.org" ``` If you plan on testing the integration with a homeserver, you will need to run an HTTPS reverse proxy in front of it @@ -58,4 +63,3 @@ as the homeserver implementation seems to require a HTTPS connection to an ID se # TODO - Deb package - Docker container -- Auto-discovery of matrix ids based on server name and username-like attribute diff --git a/build.gradle b/build.gradle index 567021a..46ecc13 100644 --- a/build.gradle +++ b/build.gradle @@ -51,6 +51,9 @@ dependencies { // LDAP connector compile 'org.apache.directory.api:api-all:1.0.0-RC2' + // DNS lookups + compile 'dnsjava:dnsjava:2.1.8' + testCompile 'junit:junit:4.12' } diff --git a/src/main/groovy/io/kamax/mxisd/lookup/DnsLookupProvider.groovy b/src/main/groovy/io/kamax/mxisd/lookup/DnsLookupProvider.groovy new file mode 100644 index 0000000..ce04990 --- /dev/null +++ b/src/main/groovy/io/kamax/mxisd/lookup/DnsLookupProvider.groovy @@ -0,0 +1,86 @@ +/* + * 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 io.kamax.mxisd.api.ThreePidType +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Component +import org.xbill.DNS.Lookup +import org.xbill.DNS.SRVRecord +import org.xbill.DNS.Type + +@Component +class DnsLookupProvider extends RemoteIdentityServerProvider { + + private Logger log = LoggerFactory.getLogger(DnsLookupProvider.class) + + @Override + int getPriority() { + return 10 + } + + @Override + Optional find(ThreePidType type, String threePid) { + log.info("Performing DNS lookup for {}", threePid) + if (ThreePidType.email != type) { + log.info("Skipping unsupported type {} for {}", type, threePid) + return Optional.empty() + } + + String domain = threePid.substring(threePid.lastIndexOf("@") + 1) + log.info("Domain name for {}: {}", threePid, domain) + + log.info("Performing SRV lookup") + String lookupDns = "_identity._matrix._tcp." + domain + log.info("Lookup name: {}", lookupDns) + + SRVRecord[] records = (SRVRecord[]) new Lookup(lookupDns, Type.SRV).run() + if (records != null) { + Arrays.sort(records, new Comparator() { + + @Override + int compare(SRVRecord o1, SRVRecord o2) { + return Integer.compare(o1.getPriority(), o2.getPriority()) + } + + }) + + for (SRVRecord record : records) { + log.info("Found SRV record: {}", record.toString()) + String baseUrl = (record.getPort() != 80 ? "https://" : "http://") + record.getTarget().toString(true) + Optional answer = find(baseUrl, type, threePid) + if (answer.isPresent()) { + return answer + } else { + log.info("No mapping found at {}", baseUrl) + } + } + } else { + log.info("No SRV record for {}", lookupDns) + } + + log.info("Performing basic lookup using domain name {}", domain) + String baseUrl = "https://" + domain + return find(baseUrl, type, threePid) + } + +} diff --git a/src/main/groovy/io/kamax/mxisd/lookup/RemoteIdentityServerProvider.groovy b/src/main/groovy/io/kamax/mxisd/lookup/RemoteIdentityServerProvider.groovy new file mode 100644 index 0000000..023e752 --- /dev/null +++ b/src/main/groovy/io/kamax/mxisd/lookup/RemoteIdentityServerProvider.groovy @@ -0,0 +1,58 @@ +/* + * 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 groovy.json.JsonException +import groovy.json.JsonSlurper +import io.kamax.mxisd.api.ThreePidType +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +abstract class RemoteIdentityServerProvider implements ThreePidProvider { + + private Logger log = LoggerFactory.getLogger(RemoteIdentityServerProvider.class) + private JsonSlurper json = new JsonSlurper() + + Optional find(String remote, ThreePidType type, String threePid) { + log.info("Looking up {} 3PID {} using {}", type, threePid, remote) + + HttpURLConnection rootSrvConn = (HttpURLConnection) new URL( + "${remote}/_matrix/identity/api/v1/lookup?medium=${type}&address=${threePid}" + ).openConnection() + + try { + def output = json.parseText(rootSrvConn.getInputStream().getText()) + if (output['address'] != null) { + return Optional.of(output) + } + + log.info("Empty 3PID mapping from {}", remote) + return Optional.empty() + } catch (IOException e) { + log.warn("Error looking up 3PID mapping {}: {}", threePid, e.getMessage()) + return Optional.empty() + } catch (JsonException e) { + log.warn("Invalid JSON answer from {}", remote) + return Optional.empty() + } + } + +} diff --git a/src/main/groovy/io/kamax/mxisd/lookup/RootProvider.groovy b/src/main/groovy/io/kamax/mxisd/lookup/RootProvider.groovy index b41400b..1eed93c 100644 --- a/src/main/groovy/io/kamax/mxisd/lookup/RootProvider.groovy +++ b/src/main/groovy/io/kamax/mxisd/lookup/RootProvider.groovy @@ -20,15 +20,13 @@ package io.kamax.mxisd.lookup -import groovy.json.JsonSlurper import io.kamax.mxisd.api.ThreePidType import org.springframework.stereotype.Component @Component -class RootProvider implements ThreePidProvider { +class RootProvider extends RemoteIdentityServerProvider { private List roots = Arrays.asList("https://matrix.org", "https://vector.im") - private JsonSlurper json = new JsonSlurper() @Override int getPriority() { @@ -38,13 +36,9 @@ class RootProvider implements ThreePidProvider { @Override Optional find(ThreePidType type, String threePid) { for (String root : roots) { - HttpURLConnection rootSrvConn = (HttpURLConnection) new URL( - "${root}/_matrix/identity/api/v1/lookup?medium=${type}&address=${threePid}" - ).openConnection() - - def output = json.parseText(rootSrvConn.getInputStream().getText()) - if (output['address'] != null) { - return Optional.of(output) + Optional answer = find(root, type, threePid) + if (answer.isPresent()) { + return answer } }