Saving current work
This commit is contained in:
@@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.config;
|
package io.kamax.mxisd.config;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
import io.kamax.mxisd.exception.ConfigurationException;
|
import io.kamax.mxisd.exception.ConfigurationException;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -28,14 +29,38 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
|||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConfigurationProperties("matrix")
|
@ConfigurationProperties("matrix")
|
||||||
public class MatrixConfig {
|
public class MatrixConfig {
|
||||||
|
|
||||||
|
public static class Identity {
|
||||||
|
private Map<String, List<String>> servers = new HashMap<>();
|
||||||
|
|
||||||
|
public Map<String, List<String>> getServers() {
|
||||||
|
return servers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServers(Map<String, List<String>> servers) {
|
||||||
|
this.servers = servers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getServers(String label) {
|
||||||
|
if (!servers.containsKey(label)) {
|
||||||
|
throw new RuntimeException("No Identity server list with label '" + label + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return servers.get(label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Logger log = LoggerFactory.getLogger(MatrixConfig.class);
|
private Logger log = LoggerFactory.getLogger(MatrixConfig.class);
|
||||||
|
|
||||||
private String domain;
|
private String domain;
|
||||||
|
private Identity identity = new Identity();
|
||||||
|
|
||||||
public String getDomain() {
|
public String getDomain() {
|
||||||
return domain;
|
return domain;
|
||||||
@@ -45,6 +70,14 @@ public class MatrixConfig {
|
|||||||
this.domain = domain;
|
this.domain = domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Identity getIdentity() {
|
||||||
|
return identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIdentity(Identity identity) {
|
||||||
|
this.identity = identity;
|
||||||
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void build() {
|
public void build() {
|
||||||
log.info("--- Matrix config ---");
|
log.info("--- Matrix config ---");
|
||||||
@@ -54,6 +87,8 @@ public class MatrixConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.info("Domain: {}", getDomain());
|
log.info("Domain: {}", getDomain());
|
||||||
|
log.info("Identity:");
|
||||||
|
log.info("\tServers: {}", new Gson().toJson(identity.getServers()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,10 +41,32 @@ public class SessionConfig {
|
|||||||
|
|
||||||
public static class PolicySource {
|
public static class PolicySource {
|
||||||
|
|
||||||
|
public static class PolicySourceRemote {
|
||||||
|
|
||||||
|
private boolean enabled;
|
||||||
|
private String server;
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getServer() {
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServer(String server) {
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
private boolean alwaysValidate;
|
|
||||||
private boolean toLocal;
|
private boolean toLocal;
|
||||||
private boolean toRemote;
|
private PolicySourceRemote toRemote = new PolicySourceRemote();
|
||||||
|
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return enabled;
|
return enabled;
|
||||||
@@ -54,14 +76,6 @@ public class SessionConfig {
|
|||||||
this.enabled = enabled;
|
this.enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAlwaysValidate() {
|
|
||||||
return alwaysValidate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAlwaysValidate(boolean alwaysValidate) {
|
|
||||||
this.alwaysValidate = alwaysValidate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean toLocal() {
|
public boolean toLocal() {
|
||||||
return toLocal;
|
return toLocal;
|
||||||
}
|
}
|
||||||
@@ -71,10 +85,14 @@ public class SessionConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean toRemote() {
|
public boolean toRemote() {
|
||||||
|
return toRemote.isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PolicySourceRemote getToRemote() {
|
||||||
return toRemote;
|
return toRemote;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setToRemote(boolean toRemote) {
|
public void setToRemote(PolicySourceRemote toRemote) {
|
||||||
this.toRemote = toRemote;
|
this.toRemote = toRemote;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,19 +129,11 @@ public class SessionConfig {
|
|||||||
public PolicySource forIf(boolean isLocal) {
|
public PolicySource forIf(boolean isLocal) {
|
||||||
return isLocal ? forLocal : forRemote;
|
return isLocal ? forLocal : forRemote;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PolicyTemplate bind = new PolicyTemplate();
|
|
||||||
private PolicyTemplate validation = new PolicyTemplate();
|
private PolicyTemplate validation = new PolicyTemplate();
|
||||||
|
|
||||||
public PolicyTemplate getBind() {
|
|
||||||
return bind;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBind(PolicyTemplate bind) {
|
|
||||||
this.bind = bind;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PolicyTemplate getValidation() {
|
public PolicyTemplate getValidation() {
|
||||||
return validation;
|
return validation;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package io.kamax.mxisd.controller.v1.remote;
|
||||||
|
|
||||||
|
import io.kamax.mxisd.session.SessionMananger;
|
||||||
|
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;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@CrossOrigin
|
||||||
|
@RequestMapping(path = RemoteIdentityAPIv1.BASE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||||
|
public class RemoteSessionController {
|
||||||
|
|
||||||
|
private Logger log = LoggerFactory.getLogger(RemoteSessionController.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SessionMananger mgr;
|
||||||
|
|
||||||
|
@RequestMapping(path = "/validate/requestToken")
|
||||||
|
public String requestToken(
|
||||||
|
HttpServletRequest request,
|
||||||
|
@RequestParam String sid,
|
||||||
|
@RequestParam("client_secret") String secret,
|
||||||
|
@RequestParam String token) {
|
||||||
|
log.info("Request {}: {}", request.getMethod(), request.getRequestURL(), request.getQueryString());
|
||||||
|
mgr.createRemote(sid, secret, token);
|
||||||
|
|
||||||
|
return "{}";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* 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.exception;
|
||||||
|
|
||||||
|
import org.apache.http.HttpStatus;
|
||||||
|
|
||||||
|
public class RemoteIdentityServerException extends MatrixException {
|
||||||
|
|
||||||
|
public RemoteIdentityServerException(String error) {
|
||||||
|
super(HttpStatus.SC_SERVICE_UNAVAILABLE, "M_REMOTE_IS_ERROR", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -25,14 +25,12 @@ import io.kamax.mxisd.lookup.SingleLookupReply
|
|||||||
import io.kamax.mxisd.lookup.SingleLookupRequest
|
import io.kamax.mxisd.lookup.SingleLookupRequest
|
||||||
import io.kamax.mxisd.lookup.ThreePidMapping
|
import io.kamax.mxisd.lookup.ThreePidMapping
|
||||||
import io.kamax.mxisd.lookup.fetcher.IRemoteIdentityServerFetcher
|
import io.kamax.mxisd.lookup.fetcher.IRemoteIdentityServerFetcher
|
||||||
|
import io.kamax.mxisd.matrix.IdentityServerUtils
|
||||||
import org.apache.commons.lang.StringUtils
|
import org.apache.commons.lang.StringUtils
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
import org.xbill.DNS.Lookup
|
|
||||||
import org.xbill.DNS.SRVRecord
|
|
||||||
import org.xbill.DNS.Type
|
|
||||||
|
|
||||||
import java.util.concurrent.ForkJoinPool
|
import java.util.concurrent.ForkJoinPool
|
||||||
import java.util.concurrent.RecursiveTask
|
import java.util.concurrent.RecursiveTask
|
||||||
@@ -64,10 +62,6 @@ class DnsLookupProvider implements IThreePidProvider {
|
|||||||
return 10
|
return 10
|
||||||
}
|
}
|
||||||
|
|
||||||
String getSrvRecordName(String domain) {
|
|
||||||
return "_matrix-identity._tcp." + domain
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<String> getDomain(String email) {
|
Optional<String> getDomain(String email) {
|
||||||
int atIndex = email.lastIndexOf("@")
|
int atIndex = email.lastIndexOf("@")
|
||||||
if (atIndex == -1) {
|
if (atIndex == -1) {
|
||||||
@@ -84,44 +78,7 @@ class DnsLookupProvider implements IThreePidProvider {
|
|||||||
return Optional.empty()
|
return Optional.empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Performing SRV lookup")
|
return IdentityServerUtils.findIsUrlForDomain(domain)
|
||||||
String lookupDns = getSrvRecordName(domain)
|
|
||||||
log.info("Lookup name: {}", lookupDns)
|
|
||||||
|
|
||||||
SRVRecord[] records = (SRVRecord[]) new Lookup(lookupDns, Type.SRV).run()
|
|
||||||
if (records != null) {
|
|
||||||
Arrays.sort(records, new Comparator<SRVRecord>() {
|
|
||||||
|
|
||||||
@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 = "https://${record.getTarget().toString(true)}:${record.getPort()}"
|
|
||||||
if (fetcher.isUsable(baseUrl)) {
|
|
||||||
log.info("Found Identity Server for domain {} at {}", domain, baseUrl)
|
|
||||||
return Optional.of(baseUrl)
|
|
||||||
} else {
|
|
||||||
log.info("{} is not a usable Identity Server", baseUrl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.info("No SRV record for {}", lookupDns)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("Performing basic lookup using domain name {}", domain)
|
|
||||||
String baseUrl = "https://" + domain
|
|
||||||
if (fetcher.isUsable(baseUrl)) {
|
|
||||||
log.info("Found Identity Server for domain {} at {}", domain, baseUrl)
|
|
||||||
return Optional.of(baseUrl)
|
|
||||||
} else {
|
|
||||||
log.info("{} is not a usable Identity Server", baseUrl)
|
|
||||||
return Optional.empty()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import io.kamax.mxisd.lookup.SingleLookupReply
|
|||||||
import io.kamax.mxisd.lookup.SingleLookupRequest
|
import io.kamax.mxisd.lookup.SingleLookupRequest
|
||||||
import io.kamax.mxisd.lookup.ThreePidMapping
|
import io.kamax.mxisd.lookup.ThreePidMapping
|
||||||
import io.kamax.mxisd.lookup.fetcher.IRemoteIdentityServerFetcher
|
import io.kamax.mxisd.lookup.fetcher.IRemoteIdentityServerFetcher
|
||||||
|
import io.kamax.mxisd.matrix.IdentityServerUtils
|
||||||
import org.apache.http.HttpEntity
|
import org.apache.http.HttpEntity
|
||||||
import org.apache.http.HttpResponse
|
import org.apache.http.HttpResponse
|
||||||
import org.apache.http.client.HttpClient
|
import org.apache.http.client.HttpClient
|
||||||
@@ -46,36 +47,13 @@ import org.springframework.stereotype.Component
|
|||||||
@Lazy
|
@Lazy
|
||||||
public class RemoteIdentityServerFetcher implements IRemoteIdentityServerFetcher {
|
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(RemoteIdentityServerFetcher.class)
|
private Logger log = LoggerFactory.getLogger(RemoteIdentityServerFetcher.class)
|
||||||
|
|
||||||
private JsonSlurper json = new JsonSlurper()
|
private JsonSlurper json = new JsonSlurper()
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean isUsable(String remote) {
|
boolean isUsable(String remote) {
|
||||||
try {
|
return IdentityServerUtils.isUsable(remote)
|
||||||
HttpURLConnection rootSrvConn = (HttpURLConnection) new URL(
|
|
||||||
"${remote}/_matrix/identity/api/v1/lookup?medium=${THREEPID_TEST_MEDIUM}&address=${THREEPID_TEST_ADDRESS}"
|
|
||||||
).openConnection()
|
|
||||||
// TODO turn this into a configuration property
|
|
||||||
rootSrvConn.setConnectTimeout(2000)
|
|
||||||
|
|
||||||
if (rootSrvConn.getResponseCode() != 200) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
def output = json.parseText(rootSrvConn.getInputStream().getText())
|
|
||||||
if (output['address']) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
} catch (IOException | JsonException e) {
|
|
||||||
log.info("{} is not a usable Identity Server: {}", remote, e.getMessage())
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
114
src/main/groovy/io/kamax/mxisd/matrix/IdentityServerUtils.java
Normal file
114
src/main/groovy/io/kamax/mxisd/matrix/IdentityServerUtils.java
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
package io.kamax.mxisd.matrix;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.xbill.DNS.Lookup;
|
||||||
|
import org.xbill.DNS.SRVRecord;
|
||||||
|
import org.xbill.DNS.TextParseException;
|
||||||
|
import org.xbill.DNS.Type;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
// FIXME placeholder, this must go in matrix-java-sdk for 1.0
|
||||||
|
public class IdentityServerUtils {
|
||||||
|
|
||||||
|
public static final String THREEPID_TEST_MEDIUM = "email";
|
||||||
|
public static final String THREEPID_TEST_ADDRESS = "mxisd-email-forever-unknown@forever-invalid.kamax.io";
|
||||||
|
|
||||||
|
private static Logger log = LoggerFactory.getLogger(IdentityServerUtils.class);
|
||||||
|
private static JsonParser parser = new JsonParser();
|
||||||
|
|
||||||
|
public static boolean isUsable(String remote) {
|
||||||
|
try {
|
||||||
|
// FIXME use Apache HTTP client
|
||||||
|
HttpURLConnection rootSrvConn = (HttpURLConnection) new URL(
|
||||||
|
remote + "/_matrix/identity/api/v1/lookup?medium=" + THREEPID_TEST_MEDIUM + "&address=" + THREEPID_TEST_ADDRESS
|
||||||
|
).openConnection();
|
||||||
|
// TODO turn this into a configuration property
|
||||||
|
rootSrvConn.setConnectTimeout(2000);
|
||||||
|
|
||||||
|
if (rootSrvConn.getResponseCode() != 200) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonElement el = parser.parse(IOUtils.toString(rootSrvConn.getInputStream(), StandardCharsets.UTF_8));
|
||||||
|
if (!el.isJsonObject()) {
|
||||||
|
log.debug("IS {} did not send back a JSON object for single 3PID lookup");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (el.getAsJsonObject().has("address")) {
|
||||||
|
log.debug("IS {} did not send back a JSON object for single 3PID lookup");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (IOException | JsonParseException e) {
|
||||||
|
log.info("{} is not a usable Identity Server: {}", remote, e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getSrvRecordName(String domain) {
|
||||||
|
return "_matrix-identity._tcp." + domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Optional<String> findIsUrlForDomain(String domainOrUrl) {
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
domainOrUrl = new URL(domainOrUrl).getHost();
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
log.info("{} is not an URL, using as-is", domainOrUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Discovery Identity Server for {}", domainOrUrl);
|
||||||
|
log.info("Performing SRV lookup");
|
||||||
|
String lookupDns = getSrvRecordName(domainOrUrl);
|
||||||
|
log.info("Lookup name: {}", lookupDns);
|
||||||
|
|
||||||
|
SRVRecord[] records = (SRVRecord[]) new Lookup(lookupDns, Type.SRV).run();
|
||||||
|
if (records != null) {
|
||||||
|
Arrays.sort(records, Comparator.comparingInt(SRVRecord::getPriority));
|
||||||
|
|
||||||
|
for (SRVRecord record : records) {
|
||||||
|
log.info("Found SRV record: {}", record.toString());
|
||||||
|
String baseUrl = "https://${record.getTarget().toString(true)}:${record.getPort()}";
|
||||||
|
if (isUsable(baseUrl)) {
|
||||||
|
log.info("Found Identity Server for domain {} at {}", domainOrUrl, baseUrl);
|
||||||
|
return Optional.of(baseUrl);
|
||||||
|
} else {
|
||||||
|
log.info("{} is not a usable Identity Server", baseUrl);
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.info("No SRV record for {}", lookupDns);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Performing basic lookup using domain name {}", domainOrUrl);
|
||||||
|
String baseUrl = "https://" + domainOrUrl;
|
||||||
|
if (isUsable(baseUrl)) {
|
||||||
|
log.info("Found Identity Server for domain {} at {}", domainOrUrl, baseUrl);
|
||||||
|
return Optional.of(baseUrl);
|
||||||
|
} else {
|
||||||
|
log.info("{} is not a usable Identity Server", baseUrl);
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
} catch (TextParseException e) {
|
||||||
|
log.warn(domainOrUrl + " is not a valid domain name");
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -20,41 +20,56 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.session;
|
package io.kamax.mxisd.session;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
import io.kamax.matrix.ThreePidMedium;
|
import io.kamax.matrix.ThreePidMedium;
|
||||||
import io.kamax.mxisd.ThreePid;
|
import io.kamax.mxisd.ThreePid;
|
||||||
import io.kamax.mxisd.config.MatrixConfig;
|
import io.kamax.mxisd.config.MatrixConfig;
|
||||||
import io.kamax.mxisd.config.SessionConfig;
|
import io.kamax.mxisd.config.SessionConfig;
|
||||||
import io.kamax.mxisd.exception.InvalidCredentialsException;
|
import io.kamax.mxisd.exception.*;
|
||||||
import io.kamax.mxisd.exception.NotAllowedException;
|
|
||||||
import io.kamax.mxisd.exception.SessionNotValidatedException;
|
|
||||||
import io.kamax.mxisd.lookup.ThreePidValidation;
|
import io.kamax.mxisd.lookup.ThreePidValidation;
|
||||||
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
|
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
|
||||||
|
import io.kamax.mxisd.matrix.IdentityServerUtils;
|
||||||
import io.kamax.mxisd.notification.NotificationManager;
|
import io.kamax.mxisd.notification.NotificationManager;
|
||||||
import io.kamax.mxisd.storage.IStorage;
|
import io.kamax.mxisd.storage.IStorage;
|
||||||
import io.kamax.mxisd.storage.dao.IThreePidSessionDao;
|
import io.kamax.mxisd.storage.dao.IThreePidSessionDao;
|
||||||
import io.kamax.mxisd.threepid.session.ThreePidSession;
|
import io.kamax.mxisd.threepid.session.ThreePidSession;
|
||||||
|
import io.kamax.mxisd.util.RestClientUtils;
|
||||||
import org.apache.commons.lang.RandomStringUtils;
|
import org.apache.commons.lang.RandomStringUtils;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClients;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static io.kamax.mxisd.config.SessionConfig.Policy.PolicyTemplate;
|
||||||
|
import static io.kamax.mxisd.config.SessionConfig.Policy.PolicyTemplate.PolicySource;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class SessionMananger {
|
public class SessionMananger {
|
||||||
|
|
||||||
private Logger log = LoggerFactory.getLogger(SessionMananger.class);
|
private Logger log = LoggerFactory.getLogger(SessionMananger.class);
|
||||||
|
|
||||||
private SessionConfig cfg;
|
private SessionConfig cfg;
|
||||||
|
private MatrixConfig mxCfg;
|
||||||
private IStorage storage;
|
private IStorage storage;
|
||||||
private LookupStrategy lookup;
|
private LookupStrategy lookup;
|
||||||
private NotificationManager notifMgr;
|
private NotificationManager notifMgr;
|
||||||
|
|
||||||
|
// FIXME export into central class, set version
|
||||||
|
private CloseableHttpClient client = HttpClients.custom().setUserAgent("mxisd").build();
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public SessionMananger(SessionConfig cfg, MatrixConfig mxCfg, IStorage storage, LookupStrategy lookup, NotificationManager notifMgr) {
|
public SessionMananger(SessionConfig cfg, MatrixConfig mxCfg, IStorage storage, LookupStrategy lookup, NotificationManager notifMgr) {
|
||||||
this.cfg = cfg;
|
this.cfg = cfg;
|
||||||
|
this.mxCfg = mxCfg;
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
this.lookup = lookup;
|
this.lookup = lookup;
|
||||||
this.notifMgr = notifMgr;
|
this.notifMgr = notifMgr;
|
||||||
@@ -91,7 +106,7 @@ public class SessionMananger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String create(String server, ThreePid tpid, String secret, int attempt, String nextLink) {
|
public String create(String server, ThreePid tpid, String secret, int attempt, String nextLink) {
|
||||||
SessionConfig.Policy.PolicyTemplate policy = cfg.getPolicy().getValidation();
|
PolicyTemplate policy = cfg.getPolicy().getValidation();
|
||||||
if (!policy.isEnabled()) {
|
if (!policy.isEnabled()) {
|
||||||
throw new NotAllowedException("Validating 3PID is disabled globally");
|
throw new NotAllowedException("Validating 3PID is disabled globally");
|
||||||
}
|
}
|
||||||
@@ -118,7 +133,7 @@ public class SessionMananger {
|
|||||||
log.info("Is 3PID bound to local domain? {}", isLocal);
|
log.info("Is 3PID bound to local domain? {}", isLocal);
|
||||||
|
|
||||||
// This might need a configuration by medium type?
|
// This might need a configuration by medium type?
|
||||||
SessionConfig.Policy.PolicyTemplate.PolicySource policySource = policy.forIf(isLocal);
|
PolicySource policySource = policy.forIf(isLocal);
|
||||||
if (!policySource.isEnabled() || (!policySource.toLocal() && !policySource.toRemote())) {
|
if (!policySource.isEnabled() || (!policySource.toLocal() && !policySource.toRemote())) {
|
||||||
log.info("Session for {}: cancelled due to policy", tpid);
|
log.info("Session for {}: cancelled due to policy", tpid);
|
||||||
throw new NotAllowedException("Validating " + (isLocal ? "local" : "remote") + " 3PID is not allowed");
|
throw new NotAllowedException("Validating " + (isLocal ? "local" : "remote") + " 3PID is not allowed");
|
||||||
@@ -170,4 +185,42 @@ public class SessionMananger {
|
|||||||
// TODO perform this if request was proxied
|
// TODO perform this if request was proxied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void createRemote(String sid, String secret, String token) {
|
||||||
|
ThreePidSession session = getSessionIfValidated(sid, secret);
|
||||||
|
|
||||||
|
boolean isLocal = isLocal(session.getThreePid());
|
||||||
|
PolicySource policy = cfg.getPolicy().getValidation().forIf(isLocal);
|
||||||
|
if (!policy.isEnabled() || !policy.toRemote()) {
|
||||||
|
throw new NotAllowedException("Validating " + (isLocal ? "local" : "remote") + " 3PID is not allowed");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> servers = mxCfg.getIdentity().getServers(policy.getToRemote().getServer());
|
||||||
|
if (servers.isEmpty()) {
|
||||||
|
throw new InternalServerError();
|
||||||
|
}
|
||||||
|
|
||||||
|
String url = IdentityServerUtils.findIsUrlForDomain(servers.get(0)).orElseThrow(InternalServerError::new);
|
||||||
|
|
||||||
|
JsonObject body = new JsonObject();
|
||||||
|
body.addProperty("client_secret", RandomStringUtils.randomAlphanumeric(16));
|
||||||
|
body.addProperty(session.getThreePid().getMedium(), session.getThreePid().getAddress());
|
||||||
|
body.addProperty("send_attempt", 1);
|
||||||
|
|
||||||
|
log.info("Creating remote 3PID session for {} with local session [{}] to {}", session.getThreePid(), sid);
|
||||||
|
HttpPost tokenReq = RestClientUtils.post(url + "/_matrix/identity/api/v1/validate/" + session.getThreePid().getMedium() + "/requestToken", body);
|
||||||
|
try (CloseableHttpResponse response = client.execute(tokenReq)) {
|
||||||
|
int status = response.getStatusLine().getStatusCode();
|
||||||
|
if (status < 200 || status >= 300) {
|
||||||
|
throw new RemoteIdentityServerException("Remote identity server returned with status " + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO finish
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.warn("Failed to create remote session with {} for {}: {}", url, session.getThreePid(), e.getMessage());
|
||||||
|
throw new RemoteIdentityServerException(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,9 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.util;
|
package io.kamax.mxisd.util;
|
||||||
|
|
||||||
|
import com.google.gson.FieldNamingPolicy;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
import org.apache.http.client.methods.HttpPost;
|
import org.apache.http.client.methods.HttpPost;
|
||||||
import org.apache.http.entity.ContentType;
|
import org.apache.http.entity.ContentType;
|
||||||
import org.apache.http.entity.StringEntity;
|
import org.apache.http.entity.StringEntity;
|
||||||
@@ -29,6 +31,8 @@ import java.nio.charset.StandardCharsets;
|
|||||||
|
|
||||||
public class RestClientUtils {
|
public class RestClientUtils {
|
||||||
|
|
||||||
|
private static Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
|
||||||
|
|
||||||
public static HttpPost post(String url, String body) {
|
public static HttpPost post(String url, String body) {
|
||||||
StringEntity entity = new StringEntity(body, StandardCharsets.UTF_8);
|
StringEntity entity = new StringEntity(body, StandardCharsets.UTF_8);
|
||||||
entity.setContentType(ContentType.APPLICATION_JSON.toString());
|
entity.setContentType(ContentType.APPLICATION_JSON.toString());
|
||||||
@@ -45,4 +49,8 @@ public class RestClientUtils {
|
|||||||
return post(url, gson.toJson(o));
|
return post(url, gson.toJson(o));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static HttpPost post(String url, Object o) {
|
||||||
|
return post(url, gson.toJson(o));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,12 @@ logging:
|
|||||||
server:
|
server:
|
||||||
port: 8090
|
port: 8090
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
identity:
|
||||||
|
servers:
|
||||||
|
root:
|
||||||
|
- 'https://matrix.org'
|
||||||
|
|
||||||
lookup:
|
lookup:
|
||||||
recursive:
|
recursive:
|
||||||
enabled: true
|
enabled: true
|
||||||
@@ -90,11 +96,15 @@ session.policy.validation:
|
|||||||
forLocal:
|
forLocal:
|
||||||
enabled: true
|
enabled: true
|
||||||
toLocal: true # This should not be changed unless you know exactly the implications!
|
toLocal: true # This should not be changed unless you know exactly the implications!
|
||||||
toRemote: true
|
toRemote:
|
||||||
|
enabled: true
|
||||||
|
server: 'root'
|
||||||
forRemote:
|
forRemote:
|
||||||
enabled: true
|
enabled: true
|
||||||
toLocal: false
|
toLocal: false
|
||||||
toRemote: true
|
toRemote:
|
||||||
|
enabled: true
|
||||||
|
server: 'root'
|
||||||
|
|
||||||
storage:
|
storage:
|
||||||
backend: 'sqlite'
|
backend: 'sqlite'
|
||||||
|
|||||||
Reference in New Issue
Block a user