Polishing, prepare for proxying 3PID sessions

This commit is contained in:
Maxime Dor
2017-09-21 07:26:33 +02:00
parent ace6019197
commit a4b4a3f24c
13 changed files with 217 additions and 119 deletions

View File

@@ -42,6 +42,7 @@ public class SessionConfig {
public static class PolicySource { public static class PolicySource {
private boolean enabled; private boolean enabled;
private boolean alwaysValidate;
private boolean toLocal; private boolean toLocal;
private boolean toRemote; private boolean toRemote;
@@ -53,6 +54,14 @@ 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;
} }
@@ -98,6 +107,10 @@ public class SessionConfig {
public PolicySource forRemote() { public PolicySource forRemote() {
return forRemote; return forRemote;
} }
public PolicySource forIf(boolean isLocal) {
return isLocal ? forLocal : forRemote;
}
} }
private PolicyTemplate bind = new PolicyTemplate(); private PolicyTemplate bind = new PolicyTemplate();

View File

@@ -45,13 +45,36 @@ public class EmailTemplateConfig {
public static class Session { public static class Session {
private String validation; public static class SessionValidation {
public String getValidation() { private String local;
private String remote;
public String getLocal() {
return local;
}
public void setLocal(String local) {
this.local = local;
}
public String getRemote() {
return remote;
}
public void setRemote(String remote) {
this.remote = remote;
}
}
private SessionValidation validation;
public SessionValidation getValidation() {
return validation; return validation;
} }
public void setValidation(String validation) { public void setValidation(SessionValidation validation) {
this.validation = validation; this.validation = validation;
} }
@@ -76,7 +99,9 @@ public class EmailTemplateConfig {
public void build() { public void build() {
log.info("--- E-mail Generator templates config ---"); log.info("--- E-mail Generator templates config ---");
log.info("Invite: {}", getName(getInvite())); log.info("Invite: {}", getName(getInvite()));
log.info("Session validation: {}", getName(getSession().getValidation())); log.info("Session validation:");
log.info("\tLocal: {}", getName(getSession().getValidation().getLocal()));
log.info("\tRemote: {}", getName(getSession().getValidation().getRemote()));
} }
} }

View File

@@ -0,0 +1,27 @@
/*
* 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.controller.v1.remote;
public class RemoteIdentityAPIv1 {
public static final String BASE = "/_matrix/identity-remote/api/v1";
}

View File

@@ -20,14 +20,13 @@
package io.kamax.mxisd.exception; package io.kamax.mxisd.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.FORBIDDEN) import org.apache.http.HttpStatus;
public class NotAllowedException extends RuntimeException {
public class NotAllowedException extends MatrixException {
public NotAllowedException(String s) { public NotAllowedException(String s) {
super(s); super(HttpStatus.SC_FORBIDDEN, "M_FORBIDDEN", s);
} }
} }

View File

@@ -31,4 +31,6 @@ public interface INotificationHandler {
void sendForValidation(IThreePidSession session); void sendForValidation(IThreePidSession session);
void sendForRemoteValidation(IThreePidSession session);
} }

View File

@@ -63,7 +63,7 @@ public class NotificationManager {
} }
public void sendforRemoteValidation(IThreePidSession session) { public void sendforRemoteValidation(IThreePidSession session) {
throw new NotImplementedException("Remote publish of 3PID bind"); ensureMedium(session.getThreePid().getMedium()).sendForRemoteValidation(session);
} }
} }

View File

@@ -114,24 +114,14 @@ public class SessionMananger {
} else { } else {
log.info("No existing session for {}", tpid); log.info("No existing session for {}", tpid);
boolean isLocalDomain = isLocal(tpid); boolean isLocal = isLocal(tpid);
log.info("Is 3PID bound to local domain? {}", isLocalDomain); log.info("Is 3PID bound to local domain? {}", isLocal);
if (isLocalDomain && (!policy.forLocal().isEnabled() || !policy.forLocal().toLocal())) { // This might need a configuration by medium type?
throw new NotAllowedException("Validating local 3PID is not allowed"); SessionConfig.Policy.PolicyTemplate.PolicySource policySource = policy.forIf(isLocal);
} if (!policySource.isEnabled() || (!policySource.toLocal() && !policySource.toRemote())) {
log.info("Session for {}: cancelled due to policy", tpid);
// We lookup if the 3PID is already known locally. throw new NotAllowedException("Validating " + (isLocal ? "local" : "remote") + " 3PID is not allowed");
boolean knownLocal = isKnownLocal(tpid);
log.info("Mapping with {} is " + (knownLocal ? "already" : "not") + " known locally", tpid);
if (!isLocalDomain && (
!policy.forRemote().isEnabled() || (
!policy.forRemote().toLocal() &&
!policy.forRemote().toRemote()
)
)) {
throw new NotAllowedException("Validating unknown remote 3PID is not allowed");
} }
String sessionId; String sessionId;
@@ -144,14 +134,12 @@ public class SessionMananger {
log.info("Generated new session {} to validate {} from server {}", sessionId, tpid, server); log.info("Generated new session {} to validate {} from server {}", sessionId, tpid, server);
// This might need a configuration by medium type? // This might need a configuration by medium type?
if (!isLocalDomain) { if (policySource.toLocal()) {
if (policy.forRemote().toLocal() && policy.forRemote().toRemote()) { log.info("Session {} for {}: sending local validation notification", sessionId, tpid);
log.info("Session {} for {}: sending local validation notification", sessionId, tpid); notifMgr.sendForValidation(session);
notifMgr.sendForValidation(session); } else {
} else { log.info("Session {} for {}: sending remote-only validation notification", sessionId, tpid);
log.info("Session {} for {}: sending remote-only validation notification", sessionId, tpid); notifMgr.sendforRemoteValidation(session);
notifMgr.sendforRemoteValidation(session);
}
} }
storage.insertThreePidSession(session.getDao()); storage.insertThreePidSession(session.getDao());

View File

@@ -33,6 +33,6 @@ public interface INotificationGenerator {
String getForValidation(IThreePidSession session); String getForValidation(IThreePidSession session);
String getForRemotePublishingValidation(IThreePidSession session); String getForRemoteValidation(IThreePidSession session);
} }

View File

@@ -26,8 +26,8 @@ import io.kamax.mxisd.config.ServerConfig;
import io.kamax.mxisd.config.threepid.medium.EmailConfig; import io.kamax.mxisd.config.threepid.medium.EmailConfig;
import io.kamax.mxisd.config.threepid.medium.EmailTemplateConfig; import io.kamax.mxisd.config.threepid.medium.EmailTemplateConfig;
import io.kamax.mxisd.controller.v1.IdentityAPIv1; import io.kamax.mxisd.controller.v1.IdentityAPIv1;
import io.kamax.mxisd.controller.v1.remote.RemoteIdentityAPIv1;
import io.kamax.mxisd.exception.InternalServerError; import io.kamax.mxisd.exception.InternalServerError;
import io.kamax.mxisd.exception.NotImplementedException;
import io.kamax.mxisd.invitation.IThreePidInviteReply; import io.kamax.mxisd.invitation.IThreePidInviteReply;
import io.kamax.mxisd.threepid.session.IThreePidSession; import io.kamax.mxisd.threepid.session.IThreePidSession;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
@@ -121,8 +121,9 @@ public class EmailNotificationGenerator implements IEmailNotificationGenerator {
@Override @Override
public String getForValidation(IThreePidSession session) { public String getForValidation(IThreePidSession session) {
log.info("Generating notification content for 3PID Session validation"); log.info("Generating notification content for 3PID Session validation");
String templateBody = getTemplateAndPopulate(templateCfg.getSession().getValidation(), session.getThreePid()); String templateBody = getTemplateAndPopulate(templateCfg.getSession().getValidation().getLocal(), session.getThreePid());
// FIXME should have a global link builder, most likely in the SDK?
String validationLink = srvCfg.getPublicUrl() + IdentityAPIv1.BASE + String validationLink = srvCfg.getPublicUrl() + IdentityAPIv1.BASE +
"/validate/" + session.getThreePid().getMedium() + "/validate/" + session.getThreePid().getMedium() +
"/submitToken?sid=" + session.getId() + "&client_secret=" + session.getSecret() + "/submitToken?sid=" + session.getId() + "&client_secret=" + session.getSecret() +
@@ -135,8 +136,19 @@ public class EmailNotificationGenerator implements IEmailNotificationGenerator {
} }
@Override @Override
public String getForRemotePublishingValidation(IThreePidSession session) { public String getForRemoteValidation(IThreePidSession session) {
throw new NotImplementedException(""); log.info("Generating notification content for 3PID Session validation");
String templateBody = getTemplateAndPopulate(templateCfg.getSession().getValidation().getLocal(), session.getThreePid());
// FIXME should have a global link builder, specific to mxisd
String nextStepLink = srvCfg.getPublicUrl() + RemoteIdentityAPIv1.BASE +
"/validate/requestToken?sid=" + session.getId() + "&client_secret=" + session.getSecret();
templateBody = templateBody.replace("%SESSION_ID%", session.getId());
templateBody = templateBody.replace("%SESSION_SECRET%", session.getSecret());
templateBody = templateBody.replace("%NEXT_STEP_LINK%", nextStepLink);
return templateBody;
} }
} }

View File

@@ -43,6 +43,7 @@ public class EmailNotificationHandler implements INotificationHandler {
@Autowired @Autowired
public EmailNotificationHandler(EmailConfig cfg, List<IEmailNotificationGenerator> generators, List<IEmailConnector> connectors) { public EmailNotificationHandler(EmailConfig cfg, List<IEmailNotificationGenerator> generators, List<IEmailConnector> connectors) {
this.cfg = cfg; this.cfg = cfg;
generator = generators.stream() generator = generators.stream()
.filter(o -> StringUtils.equals(cfg.getGenerator(), o.getId())) .filter(o -> StringUtils.equals(cfg.getGenerator(), o.getId()))
.findFirst() .findFirst()
@@ -59,24 +60,28 @@ public class EmailNotificationHandler implements INotificationHandler {
return ThreePidMedium.Email.getId(); return ThreePidMedium.Email.getId();
} }
@Override private void send(String recipient, String content) {
public void sendForInvite(IThreePidInviteReply invite) {
connector.send( connector.send(
cfg.getIdentity().getFrom(), cfg.getIdentity().getFrom(),
cfg.getIdentity().getName(), cfg.getIdentity().getName(),
invite.getInvite().getAddress(), recipient,
generator.getForInvite(invite) content
); );
} }
@Override
public void sendForInvite(IThreePidInviteReply invite) {
send(invite.getInvite().getAddress(), generator.getForInvite(invite));
}
@Override @Override
public void sendForValidation(IThreePidSession session) { public void sendForValidation(IThreePidSession session) {
connector.send( send(session.getThreePid().getAddress(), generator.getForValidation(session));
cfg.getIdentity().getFrom(), }
cfg.getIdentity().getName(),
session.getThreePid().getAddress(), @Override
generator.getForValidation(session) public void sendForRemoteValidation(IThreePidSession session) {
); send(session.getThreePid().getAddress(), generator.getForRemoteValidation(session));
} }
} }

View File

@@ -81,17 +81,19 @@ threepid:
template: template:
invite: 'classpath:email/invite-template.eml' invite: 'classpath:email/invite-template.eml'
session: session:
validation: 'classpath:email/validate-template.eml' validation:
local: 'classpath:email/validate-local-template.eml'
remote: 'classpath:email/validate-remote-template.eml'
session.policy.validation: session.policy.validation:
enabled: true enabled: true
forLocal: forLocal:
enabled: true enabled: true
toLocal: true toLocal: true # This should not be changed unless you know exactly the implications!
toRemote: true toRemote: true
forRemote: forRemote:
enabled: true enabled: true
toLocal: true # This should not be changed unless you know exactly the implications! toLocal: true
toRemote: true toRemote: true
storage: storage:

View File

@@ -0,0 +1,91 @@
Subject: Your Matrix Validation Token
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.
If it was really you who made this request, you can click on the following link to
complete the verification of your email address:
%VALIDATION_LINK%
If you didn't make this request, you can safely disregard this email.
Thanks!
%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>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>
<p><a href="%VALIDATION_LINK%">Complete email verification</a></p>
<p>If you didn't make this request, you can safely disregard this email.</p>
<p>Thanks!</p>
<p>%DOMAIN_PRETTY% Admins</p>
</td>
<td></td>
</tr>
</table>
</body>
</html>
--M3yzHl5YZehm9v4bAM8sKEdcOoVnRnKR--
--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ--

View File

@@ -1,66 +0,0 @@
Subject: Your Matrix Validation Token
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ"
--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ
Content-Type: text/plain; charset=UTF-8
Content-Disposition: inline
Hello,
We have received a request to link this email address with a Matrix account.
If this was you who made this request, you may use the following link to complete the verification of your email address:
%VALIDATION_LINK%
If your client requires a code, the code is %VALIDATION_TOKEN%
If you aren't aware of making such a request, please disregard this email.
Regards,
%DOMAIN_PRETTY% Admins
--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ
Content-Type: text/html; charset=UTF-8
Content-Disposition: inline
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<style>
body {
font-family: "Myriad Pro", "Myriad", Helvetica, Arial, sans-serif;
font-size: 12pt;
margin: 0px;
}
</style>
</head>
<body>
<p>Hello,</p>
<p>We have received a request to link this email address with a Matrix account.
If this was you who made this request, you may use the following link to
complete the verification of your email address:</p>
<p><a href="%VALIDATION_LINK%">Complete email verification</a></p>
<p>...or copy this link into your web browser:</p>
<p>%VALIDATION_LINK%</p>
<p>If your client requires a code, the code is %VALIDATION_TOKEN%</p>
<p>If you aren't aware of making such a request, please disregard this
email.</p>
<br>
<p>Regards,<br>
%DOMAIN_PRETTY% Admins</p>
</body>
</html>
--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ--