Revamp 3PID sessions

- Fix #93
- Fix #98
This commit is contained in:
Max Dor
2019-02-04 05:26:33 +01:00
parent 3e240fe34d
commit 3bebb33147
30 changed files with 336 additions and 1372 deletions

View File

@@ -86,8 +86,6 @@ public class HttpMxisd {
.get(SessionTpidGetValidatedHandler.Path, SaneHandler.around(new SessionTpidGetValidatedHandler(m.getSession())))
.post(SessionTpidBindHandler.Path, SaneHandler.around(new SessionTpidBindHandler(m.getSession(), m.getInvitationManager())))
.post(SessionTpidUnbindHandler.Path, SaneHandler.around(new SessionTpidUnbindHandler(m.getSession())))
.get(RemoteIdentityAPIv1.SESSION_REQUEST_TOKEN, SaneHandler.around(new RemoteSessionStartHandler(m.getSession(), m.getConfig().getView())))
.get(RemoteIdentityAPIv1.SESSION_CHECK, SaneHandler.around(new RemoteSessionCheckHandler(m.getSession(), m.getConfig().getView())))
// Profile endpoints
.get(ProfileHandler.Path, SaneHandler.around(new ProfileHandler(m.getProfile())))

View File

@@ -45,7 +45,7 @@ import io.kamax.mxisd.notification.NotificationHandlers;
import io.kamax.mxisd.notification.NotificationManager;
import io.kamax.mxisd.profile.ProfileManager;
import io.kamax.mxisd.profile.ProfileProviders;
import io.kamax.mxisd.session.SessionMananger;
import io.kamax.mxisd.session.SessionManager;
import io.kamax.mxisd.storage.IStorage;
import io.kamax.mxisd.storage.ormlite.OrmLiteSqlStorage;
import org.apache.http.impl.client.CloseableHttpClient;
@@ -72,7 +72,7 @@ public class Mxisd {
protected InvitationManager invMgr;
protected ProfileManager pMgr;
protected AppSvcManager asHander;
protected SessionMananger sessMgr;
protected SessionManager sessMgr;
protected NotificationManager notifMgr;
public Mxisd(MxisdConfig cfg) {
@@ -102,7 +102,7 @@ public class Mxisd {
idStrategy = new RecursivePriorityLookupStrategy(cfg.getLookup(), ThreePidProviders.get(), bridgeFetcher);
pMgr = new ProfileManager(ProfileProviders.get(), clientDns, httpClient);
notifMgr = new NotificationManager(cfg.getNotification(), NotificationHandlers.get());
sessMgr = new SessionMananger(cfg.getSession(), cfg.getMatrix(), store, notifMgr, idStrategy, httpClient);
sessMgr = new SessionManager(cfg.getSession(), cfg.getMatrix(), store, notifMgr, idStrategy, httpClient);
invMgr = new InvitationManager(cfg.getInvite(), store, idStrategy, signMgr, fedDns, notifMgr);
authMgr = new AuthManager(cfg, AuthProviders.get(), idStrategy, invMgr, clientDns, httpClient);
dirMgr = new DirectoryManager(cfg.getDirectory(), clientDns, httpClient, DirectoryProviders.get());
@@ -137,7 +137,7 @@ public class Mxisd {
return authMgr;
}
public SessionMananger getSession() {
public SessionManager getSession() {
return sessMgr;
}

View File

@@ -32,68 +32,7 @@ public class SessionConfig {
public static class PolicyTemplate {
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 toLocal;
private PolicySourceRemote toRemote = new PolicySourceRemote();
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean toLocal() {
return toLocal;
}
public void setToLocal(boolean toLocal) {
this.toLocal = toLocal;
}
public boolean toRemote() {
return toRemote.isEnabled();
}
public PolicySourceRemote getToRemote() {
return toRemote;
}
public void setToRemote(PolicySourceRemote toRemote) {
this.toRemote = toRemote;
}
}
private boolean enabled;
private PolicySource forLocal = new PolicySource();
private PolicySource forRemote = new PolicySource();
public boolean isEnabled() {
return enabled;
@@ -103,26 +42,6 @@ public class SessionConfig {
this.enabled = enabled;
}
public PolicySource getForLocal() {
return forLocal;
}
public PolicySource forLocal() {
return forLocal;
}
public PolicySource getForRemote() {
return forRemote;
}
public PolicySource forRemote() {
return forRemote;
}
public PolicySource forIf(boolean isLocal) {
return isLocal ? forLocal : forRemote;
}
}
public static class PolicyUnbind {
@@ -155,15 +74,6 @@ public class SessionConfig {
public Policy() {
validation.enabled = true;
validation.forLocal.enabled = true;
validation.forLocal.toLocal = true;
validation.forLocal.toRemote.enabled = true;
validation.forLocal.toRemote.server = "matrix-org";
validation.forRemote.enabled = true;
validation.forRemote.toLocal = false;
validation.forRemote.toRemote.enabled = true;
validation.forRemote.toRemote.server = "matrix-org";
}
private PolicyTemplate validation = new PolicyTemplate();

View File

@@ -21,12 +21,13 @@
package io.kamax.mxisd.config;
import io.kamax.matrix.json.GsonUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ViewConfig {
private transient final Logger log = LoggerFactory.getLogger(ViewConfig.class);
private static final Logger log = LoggerFactory.getLogger(ViewConfig.class);
public static class Session {
@@ -67,45 +68,13 @@ public class ViewConfig {
}
public static class Remote {
private Paths onRequest = new Paths();
private Paths onCheck = new Paths();
public Paths getOnRequest() {
return onRequest;
}
public void setOnRequest(Paths onRequest) {
this.onRequest = onRequest;
}
public Paths getOnCheck() {
return onCheck;
}
public void setOnCheck(Paths onCheck) {
this.onCheck = onCheck;
}
}
// Legacy option
private Local local = new Local();
private Local localRemote = new Local();
private Remote remote = new Remote();
private Paths onTokenSubmit = new Paths();
public Session() {
local.onTokenSubmit.success = "classpath:/templates/session/local/tokenSubmitSuccess.html";
local.onTokenSubmit.failure = "classpath:/templates/session/local/tokenSubmitFailure.html";
localRemote.onTokenSubmit.success = "classpath:/templates/session/localRemote/tokenSubmitSuccess.html";
localRemote.onTokenSubmit.failure = "classpath:/templates/session/local/tokenSubmitFailure.html";
remote.onRequest.success = "classpath:/templates/session/remote/requestSuccess.html";
remote.onRequest.failure = "classpath:/templates/session/remote/requestFailure.html";
remote.onCheck.success = "classpath:/templates/session/remote/checkSuccess.html";
remote.onCheck.failure = "classpath:/templates/session/remote/checkFailure.html";
onTokenSubmit.success = "classpath:/templates/session/tokenSubmitSuccess.html";
onTokenSubmit.failure = "classpath:/templates/session/tokenSubmitFailure.html";
}
public Local getLocal() {
@@ -116,21 +85,14 @@ public class ViewConfig {
this.local = local;
}
public Local getLocalRemote() {
return localRemote;
public Paths getOnTokenSubmit() {
return onTokenSubmit;
}
public void setLocalRemote(Local localRemote) {
this.localRemote = localRemote;
public void setOnTokenSubmit(Paths onTokenSubmit) {
this.onTokenSubmit = onTokenSubmit;
}
public Remote getRemote() {
return remote;
}
public void setRemote(Remote remote) {
this.remote = remote;
}
}
private Session session = new Session();
@@ -144,6 +106,17 @@ public class ViewConfig {
}
public void build() {
if (StringUtils.isNotBlank(session.local.onTokenSubmit.success) && StringUtils.isBlank(session.onTokenSubmit.success)) {
log.warn("Legacy option session.local.onTokenSubmit.success in use, please switch to session.onTokenSubmit.success");
session.onTokenSubmit.success = session.local.onTokenSubmit.success;
}
if (StringUtils.isNotBlank(session.local.onTokenSubmit.failure) && StringUtils.isBlank(session.onTokenSubmit.failure)) {
log.warn("Legacy option session.local.onTokenSubmit.failure in use, please switch to session.onTokenSubmit.failure");
session.onTokenSubmit.failure = session.local.onTokenSubmit.failure;
}
log.info("--- View config ---");
log.info("Session: {}", GsonUtil.get().toJson(session));
}

View File

@@ -1,37 +0,0 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2017 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.undertow.handler.identity.v1;
public class RemoteIdentityAPIv1 {
public static final String BASE = "/_matrix/identity/remote/api/v1";
public static final String SESSION_REQUEST_TOKEN = BASE + "/validate/requestToken";
public static final String SESSION_CHECK = BASE + "/validate/check";
public static String getRequestToken(String id, String secret) {
return SESSION_REQUEST_TOKEN + "?sid=" + id + "&client_secret=" + secret;
}
public static String getSessionCheck(String id, String secret) {
return SESSION_CHECK + "?sid=" + id + "&client_secret=" + secret;
}
}

View File

@@ -1,56 +0,0 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 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.undertow.handler.identity.v1;
import io.kamax.mxisd.config.ViewConfig;
import io.kamax.mxisd.exception.SessionNotValidatedException;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.kamax.mxisd.session.SessionMananger;
import io.kamax.mxisd.util.FileUtil;
import io.undertow.server.HttpServerExchange;
public class RemoteSessionCheckHandler extends BasicHttpHandler {
private SessionMananger mgr;
private ViewConfig viewCfg;
public RemoteSessionCheckHandler(SessionMananger mgr, ViewConfig viewCfg) {
this.mgr = mgr;
this.viewCfg = viewCfg;
}
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
String sid = getQueryParameter(exchange, "sid");
String secret = getQueryParameter(exchange, "client_secret");
String viewData;
try {
mgr.validateRemote(sid, secret);
viewData = FileUtil.load(viewCfg.getSession().getRemote().getOnCheck().getSuccess());
} catch (SessionNotValidatedException e) {
viewData = FileUtil.load(viewCfg.getSession().getRemote().getOnCheck().getFailure());
}
writeBodyAsUtf8(exchange, viewData);
}
}

View File

@@ -1,51 +0,0 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 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.undertow.handler.identity.v1;
import io.kamax.mxisd.config.ViewConfig;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.kamax.mxisd.session.SessionMananger;
import io.kamax.mxisd.threepid.session.IThreePidSession;
import io.kamax.mxisd.util.FileUtil;
import io.undertow.server.HttpServerExchange;
public class RemoteSessionStartHandler extends BasicHttpHandler {
private SessionMananger mgr;
private ViewConfig viewCfg;
public RemoteSessionStartHandler(SessionMananger mgr, ViewConfig viewCfg) {
this.mgr = mgr;
this.viewCfg = viewCfg;
}
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
String sid = getQueryParameter(exchange, "sid");
String secret = getQueryParameter(exchange, "client_secret");
IThreePidSession session = mgr.createRemote(sid, secret);
String rawData = FileUtil.load(viewCfg.getSession().getRemote().getOnRequest().getSuccess());
String data = rawData.replace("${checkLink}", RemoteIdentityAPIv1.getSessionCheck(session.getId(), session.getSecret()));
writeBodyAsUtf8(exchange, data);
}
}

View File

@@ -28,7 +28,7 @@ import io.kamax.mxisd.http.io.identity.RequestTokenResponse;
import io.kamax.mxisd.http.io.identity.SessionEmailTokenRequestJson;
import io.kamax.mxisd.http.io.identity.SessionPhoneTokenRequestJson;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.kamax.mxisd.session.SessionMananger;
import io.kamax.mxisd.session.SessionManager;
import io.undertow.server.HttpServerExchange;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
@@ -41,9 +41,9 @@ public class SessionStartHandler extends BasicHttpHandler {
private transient final Logger log = LoggerFactory.getLogger(SessionStartHandler.class);
private SessionMananger mgr;
private SessionManager mgr;
public SessionStartHandler(SessionMananger mgr) {
public SessionStartHandler(SessionManager mgr) {
this.mgr = mgr;
}

View File

@@ -26,7 +26,7 @@ import io.kamax.mxisd.http.IsAPIv1;
import io.kamax.mxisd.http.io.identity.BindRequest;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.kamax.mxisd.invitation.InvitationManager;
import io.kamax.mxisd.session.SessionMananger;
import io.kamax.mxisd.session.SessionManager;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.QueryParameterUtils;
import org.apache.commons.lang.StringUtils;
@@ -44,10 +44,10 @@ public class SessionTpidBindHandler extends BasicHttpHandler {
private transient final Logger log = LoggerFactory.getLogger(SessionTpidBindHandler.class);
private SessionMananger mgr;
private SessionManager mgr;
private InvitationManager invMgr;
public SessionTpidBindHandler(SessionMananger mgr, InvitationManager invMgr) {
public SessionTpidBindHandler(SessionManager mgr, InvitationManager invMgr) {
this.mgr = mgr;
this.invMgr = invMgr;
}

View File

@@ -25,7 +25,7 @@ import io.kamax.mxisd.exception.SessionNotValidatedException;
import io.kamax.mxisd.http.IsAPIv1;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.kamax.mxisd.lookup.ThreePidValidation;
import io.kamax.mxisd.session.SessionMananger;
import io.kamax.mxisd.session.SessionManager;
import io.undertow.server.HttpServerExchange;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -34,11 +34,11 @@ public class SessionTpidGetValidatedHandler extends BasicHttpHandler {
public static final String Path = IsAPIv1.Base + "/3pid/getValidated3pid";
private transient final Logger log = LoggerFactory.getLogger(SessionTpidGetValidatedHandler.class);
private static final Logger log = LoggerFactory.getLogger(SessionTpidGetValidatedHandler.class);
private SessionMananger mgr;
private SessionManager mgr;
public SessionTpidGetValidatedHandler(SessionMananger mgr) {
public SessionTpidGetValidatedHandler(SessionManager mgr) {
this.mgr = mgr;
}

View File

@@ -23,27 +23,23 @@ package io.kamax.mxisd.http.undertow.handler.identity.v1;
import com.google.gson.JsonObject;
import io.kamax.mxisd.http.IsAPIv1;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.kamax.mxisd.session.SessionMananger;
import io.kamax.mxisd.session.SessionManager;
import io.undertow.server.HttpServerExchange;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SessionTpidUnbindHandler extends BasicHttpHandler {
public static final String Path = IsAPIv1.Base + "/3pid/unbind";
private static final Logger log = LoggerFactory.getLogger(SessionTpidUnbindHandler.class);
private final SessionManager sessionMgr;
private final SessionMananger sessMgr;
public SessionTpidUnbindHandler(SessionMananger sessMgr) {
this.sessMgr = sessMgr;
public SessionTpidUnbindHandler(SessionManager sessionMgr) {
this.sessionMgr = sessionMgr;
}
@Override
public void handleRequest(HttpServerExchange exchange) {
JsonObject body = parseJsonObject(exchange);
sessMgr.unbind(body);
sessionMgr.unbind(body);
writeBodyAsUtf8(exchange, "{}");
}

View File

@@ -25,7 +25,7 @@ import io.kamax.mxisd.config.ViewConfig;
import io.kamax.mxisd.http.IsAPIv1;
import io.kamax.mxisd.http.io.identity.SuccessStatusJson;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.kamax.mxisd.session.SessionMananger;
import io.kamax.mxisd.session.SessionManager;
import io.kamax.mxisd.session.ValidationResult;
import io.kamax.mxisd.util.FileUtil;
import io.undertow.server.HttpServerExchange;
@@ -44,11 +44,11 @@ public class SessionValidateHandler extends BasicHttpHandler {
private transient final Logger log = LoggerFactory.getLogger(SessionValidateHandler.class);
private SessionMananger mgr;
private SessionManager mgr;
private ServerConfig srvCfg;
private ViewConfig viewCfg;
public SessionValidateHandler(SessionMananger mgr, ServerConfig srvCfg, ViewConfig viewCfg) {
public SessionValidateHandler(SessionManager mgr, ServerConfig srvCfg, ViewConfig viewCfg) {
this.mgr = mgr;
this.srvCfg = srvCfg;
this.viewCfg = viewCfg;
@@ -72,11 +72,11 @@ public class SessionValidateHandler extends BasicHttpHandler {
if (isHtmlRequest) {
handleHtmlRequest(exchange, medium, sid, secret, token);
} else {
handleJsonRequest(exchange, medium, sid, secret, token);
handleJsonRequest(exchange, sid, secret, token);
}
}
public void handleHtmlRequest(HttpServerExchange exchange, String medium, String sid, String secret, String token) {
private void handleHtmlRequest(HttpServerExchange exchange, String medium, String sid, String secret, String token) {
log.info("Validating session {} for medium {}", sid, medium);
ValidationResult r = mgr.validate(sid, secret, token);
log.info("Session {} was validated", sid);
@@ -93,24 +93,18 @@ public class SessionValidateHandler extends BasicHttpHandler {
exchange.getResponseHeaders().add(HttpString.tryFromString("Location"), url);
} else {
try {
String rawData = FileUtil.load(viewCfg.getSession().getLocalRemote().getOnTokenSubmit().getSuccess());
if (r.isCanRemote()) {
String url = srvCfg.getPublicUrl() + RemoteIdentityAPIv1.getRequestToken(r.getSession().getId(), r.getSession().getSecret());
String data = rawData.replace("${remoteSessionLink}", url);
writeBodyAsUtf8(exchange, data);
} else {
writeBodyAsUtf8(exchange, rawData);
}
String data = FileUtil.load(viewCfg.getSession().getOnTokenSubmit().getSuccess());
writeBodyAsUtf8(exchange, data);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public void handleJsonRequest(HttpServerExchange exchange, String medium, String sid, String secret, String token) {
private void handleJsonRequest(HttpServerExchange exchange, String sid, String secret, String token) {
log.info("Requested: {}", exchange.getRequestURL());
ValidationResult r = mgr.validate(sid, secret, token);
mgr.validate(sid, secret, token);
log.info("Session {} was validated", sid);
respondJson(exchange, new SuccessStatusJson(true));

View File

@@ -0,0 +1,228 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2017 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.session;
import com.google.gson.JsonObject;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix.ThreePid;
import io.kamax.matrix._MatrixID;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.SessionConfig;
import io.kamax.mxisd.exception.NotAllowedException;
import io.kamax.mxisd.exception.NotImplementedException;
import io.kamax.mxisd.exception.SessionNotValidatedException;
import io.kamax.mxisd.exception.SessionUnknownException;
import io.kamax.mxisd.lookup.SingleLookupReply;
import io.kamax.mxisd.lookup.ThreePidValidation;
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
import io.kamax.mxisd.notification.NotificationManager;
import io.kamax.mxisd.storage.IStorage;
import io.kamax.mxisd.storage.dao.IThreePidSessionDao;
import io.kamax.mxisd.threepid.session.ThreePidSession;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.impl.client.CloseableHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Optional;
import static io.kamax.mxisd.config.SessionConfig.Policy.PolicyTemplate;
public class SessionManager {
private static final Logger log = LoggerFactory.getLogger(SessionManager.class);
private SessionConfig cfg;
private MatrixConfig mxCfg;
private IStorage storage;
private NotificationManager notifMgr;
private LookupStrategy lookupMgr;
// FIXME export into central class, set version
private CloseableHttpClient client;
public SessionManager(
SessionConfig cfg,
MatrixConfig mxCfg,
IStorage storage,
NotificationManager notifMgr,
LookupStrategy lookupMgr,
CloseableHttpClient client
) {
this.cfg = cfg;
this.mxCfg = mxCfg;
this.storage = storage;
this.notifMgr = notifMgr;
this.lookupMgr = lookupMgr;
this.client = client;
}
private ThreePidSession getSession(String sid, String secret) {
Optional<IThreePidSessionDao> dao = storage.getThreePidSession(sid);
if (!dao.isPresent() || !StringUtils.equals(dao.get().getSecret(), secret)) {
throw new SessionUnknownException();
}
return new ThreePidSession(dao.get());
}
private ThreePidSession getSessionIfValidated(String sid, String secret) {
ThreePidSession session = getSession(sid, secret);
if (!session.isValidated()) {
throw new SessionNotValidatedException();
}
return session;
}
public String create(String server, ThreePid tpid, String secret, int attempt, String nextLink) {
PolicyTemplate policy = cfg.getPolicy().getValidation();
if (!policy.isEnabled()) {
throw new NotAllowedException("Validating 3PID is disabled");
}
synchronized (this) {
log.info("Server {} is asking to create session for {} (Attempt #{}) - Next link: {}", server, tpid, attempt, nextLink);
Optional<IThreePidSessionDao> dao = storage.findThreePidSession(tpid, secret);
if (dao.isPresent()) {
ThreePidSession session = new ThreePidSession(dao.get());
log.info("We already have a session for {}: {}", tpid, session.getId());
if (session.getAttempt() < attempt) {
log.info("Received attempt {} is greater than stored attempt {}, sending validation communication", attempt, session.getAttempt());
notifMgr.sendForValidation(session);
log.info("Sent validation notification to {}", tpid);
session.increaseAttempt();
storage.updateThreePidSession(session.getDao());
}
return session.getId();
} else {
log.info("No existing session for {}", tpid);
String sessionId;
do {
sessionId = Long.toString(System.currentTimeMillis());
} while (storage.getThreePidSession(sessionId).isPresent());
String token = RandomStringUtils.randomNumeric(6);
ThreePidSession session = new ThreePidSession(sessionId, server, tpid, secret, attempt, nextLink, token);
log.info("Generated new session {} to validate {} from server {}", sessionId, tpid, server);
storage.insertThreePidSession(session.getDao());
log.info("Stored session {}", sessionId, tpid, server);
log.info("Session {} for {}: sending validation notification", sessionId, tpid);
notifMgr.sendForValidation(session);
return sessionId;
}
}
}
public ValidationResult validate(String sid, String secret, String token) {
ThreePidSession session = getSession(sid, secret);
log.info("Attempting validation for session {} from {}", session.getId(), session.getServer());
session.validate(token);
storage.updateThreePidSession(session.getDao());
log.info("Session {} has been validated locally", session.getId());
ValidationResult r = new ValidationResult(session);
session.getNextLink().ifPresent(r::setNextUrl);
return r;
}
public ThreePidValidation getValidated(String sid, String secret) {
ThreePidSession session = getSessionIfValidated(sid, secret);
return new ThreePidValidation(session.getThreePid(), session.getValidationTime());
}
public void bind(String sid, String secret, String mxidRaw) {
// We make sure we have an acceptable User ID
if (StringUtils.isEmpty(mxidRaw)) {
throw new IllegalArgumentException("No Matrix User ID provided");
}
// We ensure the session was validated
ThreePidSession session = getSessionIfValidated(sid, secret);
// We parse the Matrix ID as acceptable
_MatrixID mxid = MatrixID.asAcceptable(mxidRaw);
// Only accept binds if the domain matches our own
if (!StringUtils.equalsIgnoreCase(mxCfg.getDomain(), mxid.getDomain())) {
throw new NotAllowedException("Only Matrix IDs from domain " + mxCfg + " can be bound");
}
log.info("Session {}: Binding of {}:{} to Matrix ID {} is accepted",
session.getId(), session.getThreePid().getMedium(), session.getThreePid().getAddress(), mxid.getId());
}
public void unbind(JsonObject reqData) {
// TODO also check for HS header to know which domain attempting the unbind
if (reqData.entrySet().size() == 2 && reqData.has("mxid") && reqData.has("threepid")) {
/* This is a HS request to remove a 3PID and is considered:
* - An attack on user privacy
* - A baffling spec breakage requiring IS and HS 3PID info to be independent [1]
* - A baffling spec breakage that 3PID (un)bind is only one way [2]
*
* Given the lack of response on our extensive feedback on the proposal [3] which has not landed in the spec yet [4],
* We'll be denying such unbind requests and will inform users using their 3PID that a fraudulent attempt of
* removing their 3PID binding has been attempted and blocked.
*
* [1]: https://matrix.org/docs/spec/client_server/r0.4.0.html#adding-account-administrative-contact-information
* [2]: https://matrix.org/docs/spec/identity_service/r0.1.0.html#privacy
* [3]: https://docs.google.com/document/d/135g2muVxmuml0iUnLoTZxk8M2ZSt3kJzg81chGh51yg/edit
* [4]: https://github.com/matrix-org/matrix-doc/issues/1194
*/
log.warn("A remote host attempted to unbind without proper authorization. Request was denied");
if (!cfg.getPolicy().getUnbind().getFraudulent().getSendWarning()) {
log.info("Not sending notification to 3PID owner as per configuration");
} else {
log.info("Sending notification to 3PID owner as per configuration");
ThreePid tpid = GsonUtil.get().fromJson(GsonUtil.getObj(reqData, "threepid"), ThreePid.class);
Optional<SingleLookupReply> lookup = lookupMgr.findLocal(tpid.getMedium(), tpid.getAddress());
if (!lookup.isPresent()) {
log.info("No 3PID owner found, not sending any notification");
} else {
log.info("3PID owner found, sending notification");
try {
notifMgr.sendForFraudulentUnbind(tpid);
log.info("Notification sent");
} catch (NotImplementedException e) {
log.warn("Unable to send notification: {}", e.getMessage());
} catch (RuntimeException e) {
log.warn("Unable to send notification due to unknown error. See stacktrace below", e);
}
}
}
}
log.info("Denying request");
throw new NotAllowedException("You have attempted to alter 3PID bindings, which can only be done by the 3PID owner directly. " +
"We have informed the 3PID owner of your fraudulent attempt.");
}
}

View File

@@ -1,456 +0,0 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2017 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.session;
import com.google.gson.JsonObject;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix.ThreePid;
import io.kamax.matrix.ThreePidMedium;
import io.kamax.matrix._MatrixID;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.SessionConfig;
import io.kamax.mxisd.exception.*;
import io.kamax.mxisd.http.io.identity.RequestTokenResponse;
import io.kamax.mxisd.http.undertow.handler.identity.v1.RemoteIdentityAPIv1;
import io.kamax.mxisd.lookup.SingleLookupReply;
import io.kamax.mxisd.lookup.ThreePidValidation;
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
import io.kamax.mxisd.matrix.IdentityServerUtils;
import io.kamax.mxisd.notification.NotificationManager;
import io.kamax.mxisd.storage.IStorage;
import io.kamax.mxisd.storage.dao.IThreePidSessionDao;
import io.kamax.mxisd.threepid.session.IThreePidSession;
import io.kamax.mxisd.threepid.session.ThreePidSession;
import io.kamax.mxisd.util.GsonParser;
import io.kamax.mxisd.util.RestClientUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import static io.kamax.mxisd.config.SessionConfig.Policy.PolicyTemplate;
import static io.kamax.mxisd.config.SessionConfig.Policy.PolicyTemplate.PolicySource;
public class SessionMananger {
private transient final Logger log = LoggerFactory.getLogger(SessionMananger.class);
private SessionConfig cfg;
private MatrixConfig mxCfg;
private IStorage storage;
private NotificationManager notifMgr;
private LookupStrategy lookupMgr;
private GsonParser parser = new GsonParser();
private PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance(); // FIXME refactor for sessions handling their own stuff
// FIXME export into central class, set version
private CloseableHttpClient client;
public SessionMananger(
SessionConfig cfg,
MatrixConfig mxCfg,
IStorage storage,
NotificationManager notifMgr,
LookupStrategy lookupMgr,
CloseableHttpClient client
) {
this.cfg = cfg;
this.mxCfg = mxCfg;
this.storage = storage;
this.notifMgr = notifMgr;
this.lookupMgr = lookupMgr;
this.client = client;
}
private boolean isLocal(ThreePid tpid) {
if (!ThreePidMedium.Email.is(tpid.getMedium())) { // We can only handle E-mails for now
return false;
}
String domain = tpid.getAddress().split("@")[1];
return StringUtils.equalsIgnoreCase(mxCfg.getDomain(), domain);
}
private ThreePidSession getSession(String sid, String secret) {
Optional<IThreePidSessionDao> dao = storage.getThreePidSession(sid);
if (!dao.isPresent() || !StringUtils.equals(dao.get().getSecret(), secret)) {
throw new SessionUnknownException();
}
return new ThreePidSession(dao.get());
}
private ThreePidSession getSessionIfValidated(String sid, String secret) {
ThreePidSession session = getSession(sid, secret);
if (!session.isValidated()) {
throw new SessionNotValidatedException();
}
return session;
}
public String create(String server, ThreePid tpid, String secret, int attempt, String nextLink) {
PolicyTemplate policy = cfg.getPolicy().getValidation();
if (!policy.isEnabled()) {
throw new NotAllowedException("Validating 3PID is disabled globally");
}
synchronized (this) {
log.info("Server {} is asking to create session for {} (Attempt #{}) - Next link: {}", server, tpid, attempt, nextLink);
Optional<IThreePidSessionDao> dao = storage.findThreePidSession(tpid, secret);
if (dao.isPresent()) {
ThreePidSession session = new ThreePidSession(dao.get());
log.info("We already have a session for {}: {}", tpid, session.getId());
if (session.getAttempt() < attempt) {
log.info("Received attempt {} is greater than stored attempt {}, sending validation communication", attempt, session.getAttempt());
notifMgr.sendForValidation(session);
log.info("Sent validation notification to {}", tpid);
session.increaseAttempt();
storage.updateThreePidSession(session.getDao());
}
return session.getId();
} else {
log.info("No existing session for {}", tpid);
boolean isLocal = isLocal(tpid);
log.info("Is 3PID bound to local domain? {}", isLocal);
// This might need a configuration by medium type?
PolicySource policySource = policy.forIf(isLocal);
if (!policySource.isEnabled() || (!policySource.toLocal() && !policySource.toRemote())) {
log.info("Session for {}: cancelled due to policy", tpid);
throw new NotAllowedException("Validating " + (isLocal ? "local" : "remote") + " 3PID is not allowed");
}
String sessionId;
do {
sessionId = Long.toString(System.currentTimeMillis());
} while (storage.getThreePidSession(sessionId).isPresent());
String token = RandomStringUtils.randomNumeric(6);
ThreePidSession session = new ThreePidSession(sessionId, server, tpid, secret, attempt, nextLink, token);
log.info("Generated new session {} to validate {} from server {}", sessionId, tpid, server);
// This might need a configuration by medium type?
if (policySource.toLocal()) {
log.info("Session {} for {}: sending local validation notification", sessionId, tpid);
notifMgr.sendForValidation(session);
} else {
log.info("Session {} for {}: sending remote-only validation notification", sessionId, tpid);
notifMgr.sendForRemoteValidation(session);
}
storage.insertThreePidSession(session.getDao());
log.info("Stored session {}", sessionId, tpid, server);
return sessionId;
}
}
}
public ValidationResult validate(String sid, String secret, String token) {
ThreePidSession session = getSession(sid, secret);
log.info("Attempting validation for session {} from {}", session.getId(), session.getServer());
boolean isLocal = isLocal(session.getThreePid());
PolicySource policy = cfg.getPolicy().getValidation().forIf(isLocal);
if (!policy.isEnabled()) {
throw new NotAllowedException("Validating " + (isLocal ? "local" : "remote") + " 3PID is not allowed");
}
if (ThreePidMedium.PhoneNumber.is(session.getThreePid().getMedium()) && session.isValidated() && session.isRemote()) {
submitRemote(session, token);
session.validateRemote();
return new ValidationResult(session, false);
}
session.validate(token);
storage.updateThreePidSession(session.getDao());
log.info("Session {} has been validated locally", session.getId());
if (ThreePidMedium.PhoneNumber.is(session.getThreePid().getMedium()) && session.isValidated() && policy.toRemote()) {
createRemote(sid, secret);
// FIXME make the message configurable/customizable (templates?)
throw new MessageForClientException("You will receive a NEW code from another number. Enter it below");
}
// FIXME definitely doable in a nicer way
ValidationResult r = new ValidationResult(session, policy.toRemote());
if (!policy.toLocal()) {
r.setNextUrl(RemoteIdentityAPIv1.getRequestToken(sid, secret));
} else {
session.getNextLink().ifPresent(r::setNextUrl);
}
return r;
}
public ThreePidValidation getValidated(String sid, String secret) {
ThreePidSession session = getSessionIfValidated(sid, secret);
return new ThreePidValidation(session.getThreePid(), session.getValidationTime());
}
public void bind(String sid, String secret, String mxidRaw) {
if (StringUtils.isEmpty(mxidRaw)) {
throw new IllegalArgumentException("No Matrix User ID provided");
}
_MatrixID mxid = MatrixID.asAcceptable(mxidRaw);
ThreePidSession session = getSessionIfValidated(sid, secret);
if (!session.isRemote()) {
log.info("Session {} for {}: MXID {} was bound locally", sid, session.getThreePid(), mxid);
return;
}
log.info("Session {} for {}: MXID {} bind is remote", sid, session.getThreePid(), mxid);
if (!session.isRemoteValidated()) {
log.error("Session {} for {}: Not validated remotely", sid, session.getThreePid());
throw new SessionNotValidatedException();
}
log.info("Session {} for {}: Performing remote bind", sid, session.getThreePid());
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(
Arrays.asList(
new BasicNameValuePair("sid", session.getRemoteId()),
new BasicNameValuePair("client_secret", session.getRemoteSecret()),
new BasicNameValuePair("mxid", mxid.getId())
), StandardCharsets.UTF_8);
HttpPost bindReq = new HttpPost(session.getRemoteServer() + "/_matrix/identity/api/v1/3pid/bind");
bindReq.setEntity(entity);
try (CloseableHttpResponse response = client.execute(bindReq)) {
int status = response.getStatusLine().getStatusCode();
if (status < 200 || status >= 300) {
String body = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
log.error("Session {} for {}: Remote IS {} failed when trying to bind {} for remote session {}\n{}",
sid, session.getThreePid(), session.getRemoteServer(), mxid, session.getRemoteId(), body);
throw new RemoteIdentityServerException(body);
}
log.error("Session {} for {}: MXID {} was bound remotely", sid, session.getThreePid(), mxid);
} catch (IOException e) {
log.error("Session {} for {}: I/O Error when trying to bind mxid {}", sid, session.getThreePid(), mxid);
throw new RemoteIdentityServerException(e.getMessage());
}
}
public void unbind(JsonObject reqData) {
// TODO also check for HS header to know which domain attempting the unbind
if (reqData.entrySet().size() == 2 && reqData.has("mxid") && reqData.has("threepid")) {
/* This is a HS request to remove a 3PID and is considered:
* - An attack on user privacy
* - A baffling spec breakage requiring IS and HS 3PID info to be independent [1]
* - A baffling spec breakage that 3PID (un)bind is only one way [2]
*
* Given the lack of response on our extensive feedback on the proposal [3] which has not landed in the spec yet [4],
* We'll be denying such unbind requests and will inform users using their 3PID that a fraudulent attempt of
* removing their 3PID binding has been attempted and blocked.
*
* [1]: https://matrix.org/docs/spec/client_server/r0.4.0.html#adding-account-administrative-contact-information
* [2]: https://matrix.org/docs/spec/identity_service/r0.1.0.html#privacy
* [3]: https://docs.google.com/document/d/135g2muVxmuml0iUnLoTZxk8M2ZSt3kJzg81chGh51yg/edit
* [4]: https://github.com/matrix-org/matrix-doc/issues/1194
*/
log.warn("A remote host attempted to unbind without proper authorization. Request was denied");
if (!cfg.getPolicy().getUnbind().getFraudulent().getSendWarning()) {
log.info("Not sending notification to 3PID owner as per configuration");
} else {
log.info("Sending notification to 3PID owner as per configuration");
ThreePid tpid = GsonUtil.get().fromJson(GsonUtil.getObj(reqData, "threepid"), ThreePid.class);
Optional<SingleLookupReply> lookup = lookupMgr.findLocal(tpid.getMedium(), tpid.getAddress());
if (!lookup.isPresent()) {
log.info("No 3PID owner found, not sending any notification");
} else {
log.info("3PID owner found, sending notification");
try {
notifMgr.sendForFraudulentUnbind(tpid);
log.info("Notification sent");
} catch (NotImplementedException e) {
log.warn("Unable to send notification: {}", e.getMessage());
} catch (RuntimeException e) {
log.warn("Unable to send notification due to unknown error. See stacktrace below", e);
}
}
}
}
log.info("Denying request");
throw new NotAllowedException("You have attempted to alter 3PID bindings, which can only be done by the 3PID owner directly. " +
"We have informed the 3PID owner of your fraudulent attempt.");
}
public IThreePidSession createRemote(String sid, String secret) {
ThreePidSession session = getSessionIfValidated(sid, secret);
log.info("Creating remote 3PID session for {} with local session [{}] to {}", session.getThreePid(), sid);
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");
}
log.info("Remote 3PID is allowed by policy");
List<String> servers = mxCfg.getIdentity().getServers(policy.getToRemote().getServer());
if (servers.isEmpty()) {
throw new FeatureNotAvailable("Remote 3PID sessions are enabled but server list is " +
"misconstrued (invalid ID or empty list");
}
String is = servers.get(0);
String url = IdentityServerUtils.findIsUrlForDomain(is).orElse(is);
log.info("Will use IS endpoint {}", url);
String remoteSecret = session.isRemote() ? session.getRemoteSecret() : RandomStringUtils.randomAlphanumeric(16);
JsonObject body = new JsonObject();
body.addProperty("client_secret", remoteSecret);
body.addProperty(session.getThreePid().getMedium(), session.getThreePid().getAddress());
body.addProperty("send_attempt", session.increaseAndGetRemoteAttempt());
if (ThreePidMedium.PhoneNumber.is(session.getThreePid().getMedium())) {
try {
Phonenumber.PhoneNumber msisdn = phoneUtil.parse("+" + session.getThreePid().getAddress(), null);
String country = phoneUtil.getRegionCodeForNumber(msisdn).toUpperCase();
body.addProperty("phone_number", phoneUtil.format(msisdn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL));
body.addProperty("country", country);
} catch (NumberParseException e) {
throw new InternalServerError(e);
}
} else {
body.addProperty(session.getThreePid().getMedium(), session.getThreePid().getAddress());
}
log.info("Requesting remote session with attempt {}", session.getRemoteAttempt());
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) {
JsonObject obj = parser.parseOptional(response).orElseThrow(() -> new RemoteIdentityServerException("Status " + status));
throw new RemoteIdentityServerException(obj.get("errcode").getAsString() + ": " + obj.get("error").getAsString());
}
RequestTokenResponse data = new GsonParser().parse(response, RequestTokenResponse.class);
log.info("Remote Session ID: {}", data.getSid());
session.setRemoteData(url, data.getSid(), remoteSecret, 1);
storage.updateThreePidSession(session.getDao());
log.info("Updated Session {} with remote data", sid);
return session;
} catch (IOException e) {
log.warn("Failed to create remote session with {} for {}: {}", url, session.getThreePid(), e.getMessage());
throw new RemoteIdentityServerException(e.getMessage());
}
}
private void submitRemote(ThreePidSession session, String token) {
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(
Arrays.asList(
new BasicNameValuePair("sid", session.getRemoteId()),
new BasicNameValuePair("client_secret", session.getRemoteSecret()),
new BasicNameValuePair("token", token)
), StandardCharsets.UTF_8);
HttpPost submitReq = new HttpPost(session.getRemoteServer() + "/_matrix/identity/api/v1/submitToken");
submitReq.setEntity(entity);
try (CloseableHttpResponse response = client.execute(submitReq)) {
JsonObject o = new GsonParser().parse(response.getEntity().getContent());
if (!o.has("success") || !o.get("success").getAsBoolean()) {
String errcode = o.get("errcode").getAsString();
throw new RemoteIdentityServerException(errcode + ": " + o.get("error").getAsString());
}
log.info("Successfully submitted validation token for {} to {}", session.getThreePid(), session.getRemoteServer());
} catch (IOException e) {
throw new RemoteIdentityServerException(e.getMessage());
}
}
public void validateRemote(String sid, String secret) {
ThreePidSession session = getSessionIfValidated(sid, secret);
if (!session.isRemote()) {
throw new NotAllowedException("Cannot remotely validate a local session");
}
log.info("Session {} for {}: Validating remote 3PID session {} on {}", sid, session.getThreePid(), session.getRemoteId(), session.getRemoteServer());
if (session.isRemoteValidated()) {
log.info("Session {} for {}: Already remotely validated", sid, session.getThreePid());
return;
}
HttpGet validateReq = new HttpGet(session.getRemoteServer() + "/_matrix/identity/api/v1/3pid/getValidated3pid?sid=" + session.getRemoteId() + "&client_secret=" + session.getRemoteSecret());
try (CloseableHttpResponse response = client.execute(validateReq)) {
int status = response.getStatusLine().getStatusCode();
if (status < 200 || status >= 300) {
throw new RemoteIdentityServerException("Remote identity server returned with status " + status);
}
JsonObject o = new GsonParser().parse(response.getEntity().getContent());
if (o.has("errcode")) {
String errcode = o.get("errcode").getAsString();
if (StringUtils.equals("M_SESSION_NOT_VALIDATED", errcode)) {
throw new SessionNotValidatedException();
} else if (StringUtils.equals("M_NO_VALID_SESSION", errcode)) {
throw new SessionUnknownException();
} else {
throw new RemoteIdentityServerException("Unknown error while validating Remote 3PID session: " + errcode + " - " + o.get("error").getAsString());
}
}
if (o.has("validated_at")) {
ThreePid remoteThreePid = new ThreePid(o.get("medium").getAsString(), o.get("address").getAsString());
if (!session.getThreePid().equals(remoteThreePid)) { // sanity check
throw new InternalServerError("Local 3PID " + session.getThreePid() + " and remote 3PID " + remoteThreePid + " do not match for session " + session.getId());
}
log.info("Session {} for {}: Remotely validated successfully", sid, session.getThreePid());
session.validateRemote();
storage.updateThreePidSession(session.getDao());
log.info("Session {} was updated in storage", sid);
}
} catch (IOException e) {
log.warn("Session {} for {}: Failed to validated remotely on {}: {}", sid, session.getThreePid(), session.getRemoteServer(), e.getMessage());
throw new RemoteIdentityServerException(e.getMessage());
}
}
}

View File

@@ -27,22 +27,16 @@ import java.util.Optional;
public class ValidationResult {
private IThreePidSession session;
private boolean canRemote;
private String nextUrl;
public ValidationResult(IThreePidSession session, boolean canRemote) {
public ValidationResult(IThreePidSession session) {
this.session = session;
this.canRemote = canRemote;
}
public IThreePidSession getSession() {
return session;
}
public boolean isCanRemote() {
return canRemote;
}
public Optional<String> getNextUrl() {
return Optional.ofNullable(nextUrl);
}

View File

@@ -1,47 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Matrix Token Verification</title>
<style>
body {
font-family: "Myriad Pro", "Myriad", Helvetica, Arial, sans-serif;
font-size: 12pt;
margin: 1em;
}
#message {
width: 1200px;
text-align: left;
padding: 1em;
margin-bottom: 40px;
margin-left: auto;
margin-right: auto;
margin-top: 50px;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.15);
-moz-box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.15);
box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.15);
background-color: #f8f8f8;
border: 1px #ccc solid;
}
</style>
</head>
<body>
<div id="message">
<p>Verification successful!</p>
<p>Your email will remain private and you will only be discoverable with it on your own server, or any related
servers configured by your system admin.<br/>
If you would like to be globally discoverable, start the process <a href="${remoteSessionLink}">here</a>.
<br/>If you chose to start the global publication process, wait until it is done before returning to your
client.</p>
<p>If the remote process is finished, or if you do not wish to start it at this time, you can now return to your
Matrix client to complete the process.</p>
</div>
</body>
</html>

View File

@@ -1,43 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Matrix global token verification</title>
<style>
body {
font-family: "Myriad Pro", "Myriad", Helvetica, Arial, sans-serif;
font-size: 12pt;
margin: 1em;
}
#message {
width: 1200px;
text-align: left;
padding: 1em;
margin-bottom: 40px;
margin-left: auto;
margin-right: auto;
margin-top: 50px;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.15);
-moz-box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.15);
box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.15);
background-color: #f8f8f8;
border: 1px #ccc solid;
}
</style>
</head>
<body>
<div id="message">
<p>You do not seem to have validated your session with the global server. Please check your messages for one similar
to the one you received initially.<br/>
Once this is done, <a href="#">click here to continue</a></p>
<p>If this problem persists, contact your system administrator with the following info: Reference #ABC</p>
</div>
</body>
</html>

View File

@@ -1,41 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Matrix global token verification</title>
<style>
body {
font-family: "Myriad Pro", "Myriad", Helvetica, Arial, sans-serif;
font-size: 12pt;
margin: 1em;
}
#message {
width: 1200px;
text-align: left;
padding: 1em;
margin-bottom: 40px;
margin-left: auto;
margin-right: auto;
margin-top: 50px;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.15);
-moz-box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.15);
box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.15);
background-color: #f8f8f8;
border: 1px #ccc solid;
}
</style>
</head>
<body>
<div id="message">
<p>Verification successful!</p>
<p>Return to your Matrix client to complete the process and make yourself globally discoverable.</p>
</div>
</body>
</html>

View File

@@ -1,42 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Matrix global token verification</title>
<style>
body {
font-family: "Myriad Pro", "Myriad", Helvetica, Arial, sans-serif;
font-size: 12pt;
margin: 1em;
}
#message {
width: 1200px;
text-align: left;
padding: 1em;
margin-bottom: 40px;
margin-left: auto;
margin-right: auto;
margin-top: 50px;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.15);
-moz-box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.15);
box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.15);
background-color: #f8f8f8;
border: 1px #ccc solid;
}
</style>
</head>
<body>
<div id="message">
<p>The process to be globally discoverable has failed!<br/>You can try to refresh this page in a few seconds or
minutes.</p>
<p>If this problem persists, contact your system administrator with the following info: Reference #ABC</p>
</div>
</body>
</html>

View File

@@ -1,45 +0,0 @@
<!DOCTYPE html>
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8"/>
<title>Matrix global token verification</title>
<style>
body {
font-family: "Myriad Pro", "Myriad", Helvetica, Arial, sans-serif;
font-size: 12pt;
margin: 1em;
}
#message {
width: 1200px;
text-align: left;
padding: 1em;
margin-bottom: 40px;
margin-left: auto;
margin-right: auto;
margin-top: 50px;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.15);
-moz-box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.15);
box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.15);
background-color: #f8f8f8;
border: 1px #ccc solid;
}
</style>
</head>
<body>
<div id="message">
<p>The process to be globally discoverable has started. A verification token has been requested on your behalf.</p>
<p>You will receive a similar communication as the first verification message.<br/>
Follow the instructions and come back to this page once you are told to return to your Matrix client or that the
verification was successful.</p>
<p>Once the validation was successful with the global server, please follow <a th:href="${checkLink}">this link</a>
to validate it with us.</p>
</div>
</body>
</html>

View File

@@ -10,10 +10,9 @@ Content-Disposition: inline
Hi,
%SENDER_NAME_OR_ID% has invited you into a room [%ROOM_NAME_OR_ID%] on
Matrix. To join the conversation, register an account on http://%DOMAIN%
Matrix. To join the conversation, register an account on https://%DOMAIN%
You can also register an account on a public server, like Matrix.org, by going to
https://riot.im/app/#/register?%INVITE_MEDIUM%=%INVITE_ADDRESS%
You can also register an account on a public server and get in touch with them.
About Matrix:
@@ -70,10 +69,9 @@ pre, code {
<p>Hi,</p>
<p>%SENDER_NAME_OR_ID% has invited you into a room [%ROOM_NAME_OR_ID%] on
Matrix. To join the conversation, register an account on <a href="http://%DOMAIN%">%DOMAIN%</a>.</p>
Matrix. To join the conversation, register an account on <a href="https://%DOMAIN%">%DOMAIN%</a>.</p>
<p>You can also register an account on a public server, like Matrix.org, by following
<a href="https://riot.im/app/#/register?%INVITE_MEDIUM%=%INVITE_ADDRESS%">this link</a>.</p>
<pYou can also register an account on a public server and get in touch with them.</p>
<br>
<p>About Matrix:</p>

View File

@@ -1,102 +0,0 @@
Subject: Linking your Email address to your Matrix account
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ"
--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ
Content-Type: text/plain; charset=UTF-8
Content-Disposition: inline
Hello there!
We have received a request to link this email address with your Matrix account.
Due to the security policy in place, this email address can only be stored in the central Matrix Identity Server.
If you continue, your e-mail address and Matrix ID association will be made public without any current mean to be removed.
If you would still like to continue, you will need to:
1. Go to your private Public registration process page:
%VALIDATION_LINK%
2. Follow the registration process of the central Identity Server, usually another email with similar content
3. Once your email address validated with the central Identity Server, click on "Continue" on page of step #1
4. If your public association is found by our Identity server, the next step will be given to you.
If you didn't make this request, or do not want to make your address public, you can safely disregard this email.
%DOMAIN_PRETTY% Admins
--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ
Content-Type: multipart/related;
boundary="M3yzHl5YZehm9v4bAM8sKEdcOoVnRnKR";
type="text/html"
--M3yzHl5YZehm9v4bAM8sKEdcOoVnRnKR
Content-Type: text/html; charset=UTF-8
Content-Disposition: inline
<!doctype html>
<html lang="en">
<head>
<style type="text/css">
body {
margin: 0px;
}
pre, code {
word-break: break-word;
white-space: pre-wrap;
}
#page {
font-family: 'Open Sans', Helvetica, Arial, Sans-Serif;
font-color: #454545;
font-size: 12pt;
width: 100%%;
padding: 20px;
}
#inner {
width: 640px;
}
.notif_link a, .footer a {
color: #76CFA6 ! important;
}
</style>
</head>
<body>
<table id="page">
<tr>
<td></td>
<td id="inner">
<p>Hello there!</p>
<p>We have received a request to link this email address with your Matrix account.</p>
<p>Due to the security policy in place, this email address can only be stored in the central Matrix Identity Server.
If you continue, your e-mail address and Matrix ID association will be made public without any current mean to be removed.</p>
<p>If you would still like to continue, you will need to:
<ol>
<li>Go to your private <a href="%NEXT_URL%">Public registration process page</a></li>
<li>Follow the registration process of the central Identity Server, usually another email with similar content</li>
<li>Once your email address validated with the central Identity Server, click on "Continue" on page of step #1</li>
<li>If your public association is found by our Identity server, the next step will be given to you.</li>
</ol>
</p>
<p>If you didn't make this request, or do not want to make your address public, you can safely disregard this email.</p>
<p>%DOMAIN_PRETTY% Admins</p>
</td>
<td></td>
</tr>
</table>
</body>
</html>
--M3yzHl5YZehm9v4bAM8sKEdcOoVnRnKR--
--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ--

View File

@@ -9,7 +9,7 @@ Content-Disposition: inline
Hello there!
We have received a request to link this email address with your Matrix account.
You or a server on your behalf requested to validate your email.
If it was really you who made this request, you can click on the following link to
complete the verification of your email address:
@@ -66,7 +66,7 @@ pre, code {
<td id="inner">
<p>Hello there!</p>
<p>We have received a request to link this email address with your Matrix account.</p>
<p>You or a server on your behalf requested to validate your email.</p>
<p>If it was really you who made this request, you can click on the following link to
complete the verification of your email address:</p>

View File

@@ -1 +1 @@
You have been invited to a Matrix room by %SENDER_NAME_OR_ID%. Visit https://riot.im/ or any public server to join and start chatting!
You have been invited to a Matrix room by %SENDER_NAME_OR_ID%. Please get in touch with them for further instructions.

View File

@@ -1 +0,0 @@
Your phone number will be made publicly searchable. To continue, you will need to enter two codes. Your first code is %VALIDATION_TOKEN%