Basic bridge failover lookup implementation

This commit is contained in:
Maxime Dor
2017-04-26 16:28:21 +02:00
parent 86b9d4b0a8
commit bd4253a50f
10 changed files with 213 additions and 32 deletions

View File

@@ -0,0 +1,32 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.lookup.fetcher
import io.kamax.mxisd.lookup.SingleLookupRequest
import io.kamax.mxisd.lookup.ThreePidMapping
interface IBridgeFetcher {
Optional<?> find(SingleLookupRequest request)
List<ThreePidMapping> populate(List<ThreePidMapping> mappings)
}

View File

@@ -0,0 +1,33 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.lookup.fetcher
import io.kamax.mxisd.lookup.ThreePidMapping
interface IRemoteIdentityServerFetcher {
boolean isUsable(String remote)
Optional<?> find(String remote, String type, String threePid)
List<ThreePidMapping> find(String remote, List<ThreePidMapping> mappings)
}

View File

@@ -0,0 +1,73 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.lookup.provider;
import io.kamax.mxisd.config.RecursiveLookupBridgeConfig;
import io.kamax.mxisd.lookup.SingleLookupRequest;
import io.kamax.mxisd.lookup.ThreePidMapping;
import io.kamax.mxisd.lookup.fetcher.IBridgeFetcher;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@Component
public class BridgeFetcher implements IBridgeFetcher {
private Logger log = LoggerFactory.getLogger(BridgeFetcher.class);
@Autowired
private RecursiveLookupBridgeConfig cfg;
@Autowired
private RemoteIdentityServerFetcher fetcher;
@Override
public Optional<?> find(SingleLookupRequest request) {
Optional<String> 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());
} else if (!StringUtils.isBlank(cfg.getServer())) {
log.info("Using generic bridge lookup URL {}", cfg.getServer());
return fetcher.find(cfg.getServer(), request.getType(), request.getThreePid());
} else {
log.info("No bridge lookup URL found/configured, skipping");
return Optional.empty();
}
}
@Override
public List<ThreePidMapping> populate(List<ThreePidMapping> mappings) {
log.warn("Bulk lookup on bridge lookup requested, but not supported - returning empty list");
return Collections.emptyList();
}
}

View File

@@ -23,6 +23,7 @@ package io.kamax.mxisd.lookup.provider
import io.kamax.mxisd.config.ServerConfig
import io.kamax.mxisd.lookup.SingleLookupRequest
import io.kamax.mxisd.lookup.ThreePidMapping
import io.kamax.mxisd.lookup.fetcher.IRemoteIdentityServerFetcher
import org.apache.commons.lang.StringUtils
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@@ -37,13 +38,21 @@ import java.util.concurrent.RecursiveTask
import java.util.function.Function
@Component
class DnsLookupProvider extends RemoteIdentityServerProvider {
class DnsLookupFetcher implements IThreePidProvider {
private Logger log = LoggerFactory.getLogger(DnsLookupProvider.class)
private Logger log = LoggerFactory.getLogger(DnsLookupFetcher.class)
@Autowired
private ServerConfig srvCfg
@Autowired
private IRemoteIdentityServerFetcher fetcher
@Override
boolean isLocal() {
return false
}
@Override
int getPriority() {
return 10
@@ -87,7 +96,7 @@ class DnsLookupProvider extends RemoteIdentityServerProvider {
for (SRVRecord record : records) {
log.info("Found SRV record: {}", record.toString())
String baseUrl = "https://${record.getTarget().toString(true)}:${record.getPort()}"
if (isUsableIdentityServer(baseUrl)) {
if (fetcher.isUsable(baseUrl)) {
log.info("Found Identity Server for domain {} at {}", domain, baseUrl)
return Optional.of(baseUrl)
} else {
@@ -100,7 +109,7 @@ class DnsLookupProvider extends RemoteIdentityServerProvider {
log.info("Performing basic lookup using domain name {}", domain)
String baseUrl = "https://" + domain
if (isUsableIdentityServer(baseUrl)) {
if (fetcher.isUsable(baseUrl)) {
log.info("Found Identity Server for domain {} at {}", domain, baseUrl)
return Optional.of(baseUrl)
} else {
@@ -123,7 +132,7 @@ class DnsLookupProvider extends RemoteIdentityServerProvider {
Optional<String> baseUrl = findIdentityServerForDomain(domain)
if (baseUrl.isPresent()) {
return find(baseUrl.get(), request.getType().toString(), request.getThreePid())
return fetcher.find(baseUrl.get(), request.getType().toString(), request.getThreePid())
}
return Optional.empty()
@@ -205,7 +214,7 @@ class DnsLookupProvider extends RemoteIdentityServerProvider {
if (!baseUrl.isPresent()) {
log.info("No usable Identity server for domain {}", domain)
} else {
domainMappings.addAll(find(baseUrl.get(), mappings))
domainMappings.addAll(fetcher.find(baseUrl.get(), mappings))
log.info("Found {} mappings in domain {}", domainMappings.size(), domain)
}

View File

@@ -23,19 +23,28 @@ package io.kamax.mxisd.lookup.provider
import io.kamax.mxisd.config.ForwardConfig
import io.kamax.mxisd.lookup.SingleLookupRequest
import io.kamax.mxisd.lookup.ThreePidMapping
import io.kamax.mxisd.lookup.fetcher.IRemoteIdentityServerFetcher
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
@Component
class ForwarderProvider extends RemoteIdentityServerProvider {
class ForwarderFetcher implements IThreePidProvider {
private Logger log = LoggerFactory.getLogger(ForwarderProvider.class)
private Logger log = LoggerFactory.getLogger(ForwarderFetcher.class)
@Autowired
private ForwardConfig cfg
@Autowired
private IRemoteIdentityServerFetcher fetcher
@Override
boolean isLocal() {
return false
}
@Override
int getPriority() {
return 0
@@ -44,7 +53,7 @@ class ForwarderProvider extends RemoteIdentityServerProvider {
@Override
Optional<?> find(SingleLookupRequest request) {
for (String root : cfg.getServers()) {
Optional<?> answer = find(root, request.getType(), request.getThreePid())
Optional<?> answer = fetcher.find(root, request.getType(), request.getThreePid())
if (answer.isPresent()) {
return answer
}
@@ -61,7 +70,7 @@ class ForwarderProvider extends RemoteIdentityServerProvider {
for (String root : cfg.getServers()) {
log.info("{} mappings remaining: {}", mappingsToDo.size(), mappingsToDo)
log.info("Querying {}", root)
List<ThreePidMapping> mappingsFound = find(root, mappingsToDo)
List<ThreePidMapping> mappingsFound = fetcher.find(root, mappingsToDo)
log.info("{} returned {} mappings", root, mappingsFound.size())
mappingsFoundGlobal.addAll(mappingsFound)
mappingsToDo.removeAll(mappingsFound)

View File

@@ -23,7 +23,7 @@ package io.kamax.mxisd.lookup.provider
import io.kamax.mxisd.lookup.SingleLookupRequest
import io.kamax.mxisd.lookup.ThreePidMapping
interface ThreePidProvider {
interface IThreePidProvider {
boolean isLocal()

View File

@@ -37,7 +37,7 @@ import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
@Component
class LdapProvider implements ThreePidProvider {
class LdapProvider implements IThreePidProvider {
public static final String UID = "uid"
public static final String MATRIX_ID = "mxid"

View File

@@ -25,6 +25,7 @@ import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import io.kamax.mxisd.controller.v1.ClientBulkLookupRequest
import io.kamax.mxisd.lookup.ThreePidMapping
import io.kamax.mxisd.lookup.fetcher.IRemoteIdentityServerFetcher
import org.apache.http.HttpEntity
import org.apache.http.HttpResponse
import org.apache.http.client.HttpClient
@@ -34,22 +35,24 @@ import org.apache.http.entity.ContentType
import org.apache.http.impl.client.HttpClients
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.context.annotation.Lazy
import org.springframework.context.annotation.Scope
import org.springframework.stereotype.Component
abstract class RemoteIdentityServerProvider implements ThreePidProvider {
@Component
@Scope("prototype")
@Lazy
public class RemoteIdentityServerFetcher implements IRemoteIdentityServerFetcher {
public static final String THREEPID_TEST_MEDIUM = "email"
public static final String THREEPID_TEST_ADDRESS = "john.doe@example.org"
private Logger log = LoggerFactory.getLogger(RemoteIdentityServerProvider.class)
private Logger log = LoggerFactory.getLogger(RemoteIdentityServerFetcher.class)
private JsonSlurper json = new JsonSlurper()
@Override
boolean isLocal() {
return false
}
boolean isUsableIdentityServer(String remote) {
boolean isUsable(String remote) {
try {
HttpURLConnection rootSrvConn = (HttpURLConnection) new URL(
"${remote}/_matrix/identity/api/v1/lookup?medium=${THREEPID_TEST_MEDIUM}&address=${THREEPID_TEST_ADDRESS}"
@@ -73,6 +76,7 @@ abstract class RemoteIdentityServerProvider implements ThreePidProvider {
}
}
@Override
Optional<?> find(String remote, String type, String threePid) {
log.info("Looking up {} 3PID {} using {}", type, threePid, remote)
@@ -98,6 +102,7 @@ abstract class RemoteIdentityServerProvider implements ThreePidProvider {
}
}
@Override
List<ThreePidMapping> find(String remote, List<ThreePidMapping> mappings) {
List<ThreePidMapping> mappingsFound = new ArrayList<>()

View File

@@ -26,7 +26,8 @@ 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.provider.ThreePidProvider
import io.kamax.mxisd.lookup.fetcher.IBridgeFetcher
import io.kamax.mxisd.lookup.provider.IThreePidProvider
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.InitializingBean
@@ -42,7 +43,10 @@ class RecursivePriorityLookupStrategy implements LookupStrategy, InitializingBea
private RecursiveLookupConfig recursiveCfg
@Autowired
private List<ThreePidProvider> providers
private List<IThreePidProvider> providers
@Autowired
private IBridgeFetcher bridge
private List<CIDRUtils> allowedCidr = new ArrayList<>()
@@ -50,10 +54,10 @@ class RecursivePriorityLookupStrategy implements LookupStrategy, InitializingBea
void afterPropertiesSet() throws Exception {
log.info("Found ${providers.size()} providers")
providers.sort(new Comparator<ThreePidProvider>() {
providers.sort(new Comparator<IThreePidProvider>() {
@Override
int compare(ThreePidProvider o1, ThreePidProvider o2) {
int compare(IThreePidProvider o1, IThreePidProvider o2) {
return Integer.compare(o2.getPriority(), o1.getPriority())
}
@@ -66,25 +70,32 @@ class RecursivePriorityLookupStrategy implements LookupStrategy, InitializingBea
}
}
List<ThreePidProvider> listUsableProviders(ALookupRequest request) {
List<ThreePidProvider> usableProviders = new ArrayList<>()
boolean isAllowedForRecursive(String source) {
boolean canRecurse = false
if (recursiveCfg.isEnabled()) {
log.debug("Checking {} CIDRs for recursion", allowedCidr.size())
for (CIDRUtils cidr : allowedCidr) {
if (cidr.isInRange(request.getRequester())) {
log.debug("{} is in range {}, allowing recursion", request.getRequester(), cidr.getNetworkAddress())
if (cidr.isInRange(source)) {
log.debug("{} is in range {}, allowing recursion", source, cidr.getNetworkAddress())
canRecurse = true
break
} else {
log.debug("{} is not in range {}", request.getRequester(), cidr.getNetworkAddress())
log.debug("{} is not in range {}", source, cidr.getNetworkAddress())
}
}
}
return canRecurse
}
List<IThreePidProvider> listUsableProviders(ALookupRequest request) {
List<IThreePidProvider> usableProviders = new ArrayList<>()
boolean canRecurse = isAllowedForRecursive(request.getRequester())
log.info("Host {} allowed for recursion: {}", request.getRequester(), canRecurse)
for (ThreePidProvider provider : providers) {
for (IThreePidProvider provider : providers) {
if (provider.isLocal() || canRecurse) {
usableProviders.add(provider)
}
@@ -95,13 +106,18 @@ class RecursivePriorityLookupStrategy implements LookupStrategy, InitializingBea
@Override
Optional<?> find(SingleLookupRequest request) {
for (ThreePidProvider provider : listUsableProviders(request)) {
for (IThreePidProvider provider : listUsableProviders(request)) {
Optional<?> lookupDataOpt = provider.find(request)
if (lookupDataOpt.isPresent()) {
return lookupDataOpt
}
}
if (recursiveCfg.getBridge().getEnabled() && (!recursiveCfg.getBridge().getRecursiveOnly() || isAllowedForRecursive(request.getRequester()))) {
log.info("Using bridge failover for lookup")
return bridge.find(request)
}
return Optional.empty()
}
@@ -110,7 +126,7 @@ class RecursivePriorityLookupStrategy implements LookupStrategy, InitializingBea
List<ThreePidMapping> mapToDo = new ArrayList<>(request.getMappings())
List<ThreePidMapping> mapFoundAll = new ArrayList<>()
for (ThreePidProvider provider : listUsableProviders(request)) {
for (IThreePidProvider provider : listUsableProviders(request)) {
if (mapToDo.isEmpty()) {
log.info("No more mappings to lookup")
break