Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
dbc764fe65 | ||
|
d5680b2dfe | ||
|
5aad4fb81e | ||
|
a1f64f5159 | ||
|
a96920f533 |
@@ -10,6 +10,7 @@ ma1sd - Federated Matrix Identity Server
|
|||||||
- [Contribute](#contribute)
|
- [Contribute](#contribute)
|
||||||
- [Powered by ma1sd](#powered-by-ma1sd)
|
- [Powered by ma1sd](#powered-by-ma1sd)
|
||||||
- [FAQ](#faq)
|
- [FAQ](#faq)
|
||||||
|
- [Migration from mxisd](#migration-from-mxisd)
|
||||||
- [Contact](#contact)
|
- [Contact](#contact)
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -108,6 +109,10 @@ The following projects can use ma1sd under the hood for some or all their featur
|
|||||||
# FAQ
|
# FAQ
|
||||||
See the [dedicated document](docs/faq.md)
|
See the [dedicated document](docs/faq.md)
|
||||||
|
|
||||||
|
# Migration from mxisd
|
||||||
|
|
||||||
|
See the [migration guide](docs/migration-from-mxisd.md)
|
||||||
|
|
||||||
# Contact
|
# Contact
|
||||||
Get in touch via:
|
Get in touch via:
|
||||||
- Matrix: [#ma1sd:ru-matrix.org](https://matrix.to/#/#ma1sd:ru-matrix.org)
|
- Matrix: [#ma1sd:ru-matrix.org](https://matrix.to/#/#ma1sd:ru-matrix.org)
|
||||||
|
@@ -45,6 +45,14 @@ Create a list under the label `myOtherServers` containing two Identity servers:
|
|||||||
- `server.port`: HTTP port to listen on (unencrypted)
|
- `server.port`: HTTP port to listen on (unencrypted)
|
||||||
- `server.publicUrl`: Defaults to `https://{server.name}`
|
- `server.publicUrl`: Defaults to `https://{server.name}`
|
||||||
|
|
||||||
|
## Unbind (MSC1915)
|
||||||
|
- `session.policy.unbind.enabled`: Enable or disable unbind functionality (MSC1915). (Defaults to true).
|
||||||
|
|
||||||
|
*Warning*: Unbind check incoming request by two ways:
|
||||||
|
- session validation.
|
||||||
|
- request signature via `X-Matrix` header and uses `server.publicUrl` property to construct the signing json;
|
||||||
|
Commonly the `server.publicUrl` should be the same value as the `trusted_third_party_id_servers` property in the synapse config.
|
||||||
|
|
||||||
## Storage
|
## Storage
|
||||||
### SQLite
|
### SQLite
|
||||||
`storage.provider.sqlite.database`: Absolute location of the SQLite database
|
`storage.provider.sqlite.database`: Absolute location of the SQLite database
|
||||||
|
@@ -131,7 +131,7 @@ trusted_third_party_id_servers:
|
|||||||
It is **highly recommended** to remove `matrix.org` and `vector.im` (or any other default entry) from your configuration
|
It is **highly recommended** to remove `matrix.org` and `vector.im` (or any other default entry) from your configuration
|
||||||
so only your own Identity server is authoritative for your HS.
|
so only your own Identity server is authoritative for your HS.
|
||||||
|
|
||||||
## Validate
|
## Validate (Under reconstruction)
|
||||||
**NOTE:** In case your homeserver has no working federation, step 5 will not happen. If step 4 took place, consider
|
**NOTE:** In case your homeserver has no working federation, step 5 will not happen. If step 4 took place, consider
|
||||||
your installation validated.
|
your installation validated.
|
||||||
|
|
||||||
|
16
docs/migration-from-mxisd.md
Normal file
16
docs/migration-from-mxisd.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# Migration from mxisd
|
||||||
|
|
||||||
|
Version 2.0.0 of the ma1sd uses the same format of the database schema and main configuration file as mxisd.
|
||||||
|
|
||||||
|
Migration from mxisd:
|
||||||
|
- install ma1sd via deb package, docker image or zip/tar archive
|
||||||
|
- stop mxisd
|
||||||
|
- copy configuration file (by default /etc/mxisd/mxisd.yaml to /etc/ma1sd/ma1sd.yaml)
|
||||||
|
- copy key store (by default /var/lib/mxisd/keys folder to /var/lib/ma1sd/keys)
|
||||||
|
- copy storage (by default /var/lib/mxisd/store.db to /var/lib/ma1sd/store.db)
|
||||||
|
- change paths in the new config file (ma1sd.yaml). There are options: `key.path` and `storage.provider.sqlite`
|
||||||
|
- start ma1sd
|
||||||
|
|
||||||
|
Due to ma1sd uses the same ports by default as mxisd it isn't necessary to change nginx/apache configuration.
|
||||||
|
|
||||||
|
If you have any troubles with migration don't hesitate to ask questions in [#ma1sd:ru-matrix.org](https://matrix.to/#/#ma1sd:ru-matrix.org) room.
|
@@ -31,8 +31,8 @@ notification:
|
|||||||
text: <Path to file containing the raw text part of the email. Do not set to not use one>
|
text: <Path to file containing the raw text part of the email. Do not set to not use one>
|
||||||
html: <Path to file containing the HTML part of the email. Do not set to not use one>
|
html: <Path to file containing the HTML part of the email. Do not set to not use one>
|
||||||
unbind:
|
unbind:
|
||||||
fraudulent:
|
notification:
|
||||||
subject: <Subject of the email notification sent for potentially fraudulent 3PID unbinds>
|
subject: <Subject of the email notification sent for 3PID unbinds>
|
||||||
body:
|
body:
|
||||||
text: <Path to file containing the raw text part of the email. Do not set to not use one>
|
text: <Path to file containing the raw text part of the email. Do not set to not use one>
|
||||||
html: <Path to file containing the raw text part of the email. Do not set to not use one>
|
html: <Path to file containing the raw text part of the email. Do not set to not use one>
|
||||||
|
@@ -9,7 +9,7 @@ provide your own custom templates.
|
|||||||
Templates for the following events/actions are available:
|
Templates for the following events/actions are available:
|
||||||
- [3PID invite](../../features/identity.md)
|
- [3PID invite](../../features/identity.md)
|
||||||
- [3PID session: validation](../session/session.md)
|
- [3PID session: validation](../session/session.md)
|
||||||
- [3PID session: fraudulent unbind](https://github.com/kamax-matrix/ma1sd/wiki/ma1sd-and-your-privacy#improving-your-privacy-one-commit-at-the-time)
|
- [3PID session: unbind](https://github.com/kamax-matrix/ma1sd/wiki/ma1sd-and-your-privacy#improving-your-privacy-one-commit-at-the-time)
|
||||||
- [Matrix ID invite](../../features/experimental/application-service.md#email-notification-about-room-invites-by-matrix-ids)
|
- [Matrix ID invite](../../features/experimental/application-service.md#email-notification-about-room-invites-by-matrix-ids)
|
||||||
|
|
||||||
## Placeholders
|
## Placeholders
|
||||||
@@ -71,7 +71,7 @@ under the namespace `threepid.medium.<medium>.generators.template`.
|
|||||||
Under such namespace, the following keys are available:
|
Under such namespace, the following keys are available:
|
||||||
- `invite`: Path to the 3PID invite notification template
|
- `invite`: Path to the 3PID invite notification template
|
||||||
- `session.validation`: Path to the 3PID session validation notification template
|
- `session.validation`: Path to the 3PID session validation notification template
|
||||||
- `session.unbind.fraudulent`: Path to the 3PID session fraudulent unbind notification template
|
- `session.unbind`: Path to the 3PID session unbind notification template
|
||||||
- `generic.matrixId`: Path to the Matrix ID invite notification template
|
- `generic.matrixId`: Path to the Matrix ID invite notification template
|
||||||
- `placeholder`: Map of key/values to set static values for some placeholders.
|
- `placeholder`: Map of key/values to set static values for some placeholders.
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@ threepid:
|
|||||||
session:
|
session:
|
||||||
validation: '/path/to/validate-template.eml'
|
validation: '/path/to/validate-template.eml'
|
||||||
unbind:
|
unbind:
|
||||||
fraudulent: '/path/to/unbind-fraudulent-template.eml'
|
notification: '/path/to/unbind-notification-template.eml'
|
||||||
generic:
|
generic:
|
||||||
matrixId: '/path/to/mxid-invite-template.eml'
|
matrixId: '/path/to/mxid-invite-template.eml'
|
||||||
placeholder:
|
placeholder:
|
||||||
|
@@ -103,8 +103,8 @@ session:
|
|||||||
validation:
|
validation:
|
||||||
enabled: true
|
enabled: true
|
||||||
unbind:
|
unbind:
|
||||||
fraudulent:
|
notification:
|
||||||
sendWarning: true
|
enabled: true
|
||||||
|
|
||||||
# DO NOT COPY/PASTE AS-IS IN YOUR CONFIGURATION
|
# DO NOT COPY/PASTE AS-IS IN YOUR CONFIGURATION
|
||||||
# CONFIGURATION EXAMPLE
|
# CONFIGURATION EXAMPLE
|
||||||
@@ -115,11 +115,7 @@ are allowed to do in terms of 3PID sessions. The policy has a global on/off swit
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
`unbind.fraudulent` controls warning notifications if an illegal/fraudulent 3PID removal is attempted on the Identity server.
|
`unbind` controls warning notifications for 3PID removal.
|
||||||
This is directly related to synapse disregard for privacy and new GDPR laws in Europe in an attempt to inform users about
|
|
||||||
potential privacy leaks.
|
|
||||||
|
|
||||||
For more information, see the corresponding [synapse issue](https://github.com/matrix-org/synapse/issues/4540).
|
|
||||||
|
|
||||||
### Web views
|
### Web views
|
||||||
Once a user click on a validation link, it is taken to the Identity Server validation page where the token is submitted.
|
Once a user click on a validation link, it is taken to the Identity Server validation page where the token is submitted.
|
||||||
|
@@ -118,7 +118,7 @@ public class Mxisd {
|
|||||||
idStrategy = new RecursivePriorityLookupStrategy(cfg.getLookup(), ThreePidProviders.get(), bridgeFetcher);
|
idStrategy = new RecursivePriorityLookupStrategy(cfg.getLookup(), ThreePidProviders.get(), bridgeFetcher);
|
||||||
pMgr = new ProfileManager(ProfileProviders.get(), clientDns, httpClient);
|
pMgr = new ProfileManager(ProfileProviders.get(), clientDns, httpClient);
|
||||||
notifMgr = new NotificationManager(cfg.getNotification(), NotificationHandlers.get());
|
notifMgr = new NotificationManager(cfg.getNotification(), NotificationHandlers.get());
|
||||||
sessMgr = new SessionManager(cfg.getSession(), cfg.getMatrix(), store, notifMgr);
|
sessMgr = new SessionManager(cfg, store, notifMgr, resolver, httpClient, signMgr);
|
||||||
invMgr = new InvitationManager(cfg, store, idStrategy, keyMgr, signMgr, resolver, notifMgr, pMgr);
|
invMgr = new InvitationManager(cfg, store, idStrategy, keyMgr, signMgr, resolver, notifMgr, pMgr);
|
||||||
authMgr = new AuthManager(cfg, AuthProviders.get(), idStrategy, invMgr, clientDns, httpClient);
|
authMgr = new AuthManager(cfg, AuthProviders.get(), idStrategy, invMgr, clientDns, httpClient);
|
||||||
dirMgr = new DirectoryManager(cfg.getDirectory(), clientDns, httpClient, DirectoryProviders.get());
|
dirMgr = new DirectoryManager(cfg.getDirectory(), clientDns, httpClient, DirectoryProviders.get());
|
||||||
|
@@ -46,34 +46,20 @@ public class SessionConfig {
|
|||||||
|
|
||||||
public static class PolicyUnbind {
|
public static class PolicyUnbind {
|
||||||
|
|
||||||
public static class PolicyUnbindFraudulent {
|
private boolean enabled = true;
|
||||||
|
|
||||||
private boolean sendWarning = true;
|
public boolean getEnabled() {
|
||||||
|
return enabled;
|
||||||
public boolean getSendWarning() {
|
|
||||||
return sendWarning;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSendWarning(boolean sendWarning) {
|
public void setEnabled(boolean enabled) {
|
||||||
this.sendWarning = sendWarning;
|
this.enabled = enabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private PolicyUnbindFraudulent fraudulent = new PolicyUnbindFraudulent();
|
|
||||||
|
|
||||||
public PolicyUnbindFraudulent getFraudulent() {
|
|
||||||
return fraudulent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFraudulent(PolicyUnbindFraudulent fraudulent) {
|
|
||||||
this.fraudulent = fraudulent;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public Policy() {
|
public Policy() {
|
||||||
validation.enabled = true;
|
validation.enabled = true;
|
||||||
|
unbind.enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PolicyTemplate validation = new PolicyTemplate();
|
private PolicyTemplate validation = new PolicyTemplate();
|
||||||
|
@@ -115,24 +115,10 @@ public class EmailSendGridConfig {
|
|||||||
|
|
||||||
public static class Templates {
|
public static class Templates {
|
||||||
|
|
||||||
public static class TemplateSessionUnbind {
|
|
||||||
|
|
||||||
private EmailTemplate fraudulent = new EmailTemplate();
|
|
||||||
|
|
||||||
public EmailTemplate getFraudulent() {
|
|
||||||
return fraudulent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFraudulent(EmailTemplate fraudulent) {
|
|
||||||
this.fraudulent = fraudulent;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class TemplateSession {
|
public static class TemplateSession {
|
||||||
|
|
||||||
private EmailTemplate validation = new EmailTemplate();
|
private EmailTemplate validation = new EmailTemplate();
|
||||||
private TemplateSessionUnbind unbind = new TemplateSessionUnbind();
|
private EmailTemplate unbind = new EmailTemplate();
|
||||||
|
|
||||||
public EmailTemplate getValidation() {
|
public EmailTemplate getValidation() {
|
||||||
return validation;
|
return validation;
|
||||||
@@ -142,11 +128,11 @@ public class EmailSendGridConfig {
|
|||||||
this.validation = validation;
|
this.validation = validation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TemplateSessionUnbind getUnbind() {
|
public EmailTemplate getUnbind() {
|
||||||
return unbind;
|
return unbind;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUnbind(TemplateSessionUnbind unbind) {
|
public void setUnbind(EmailTemplate unbind) {
|
||||||
this.unbind = unbind;
|
this.unbind = unbind;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -31,7 +31,7 @@ public class EmailTemplateConfig extends GenericTemplateConfig {
|
|||||||
setInvite("classpath:/threepids/email/invite-template.eml");
|
setInvite("classpath:/threepids/email/invite-template.eml");
|
||||||
getGeneric().put("matrixId", "classpath:/threepids/email/mxid-template.eml");
|
getGeneric().put("matrixId", "classpath:/threepids/email/mxid-template.eml");
|
||||||
getSession().setValidation("classpath:/threepids/email/validate-template.eml");
|
getSession().setValidation("classpath:/threepids/email/validate-template.eml");
|
||||||
getSession().getUnbind().setFraudulent("classpath:/threepids/email/unbind-fraudulent.eml");
|
getSession().getUnbind().setNotification("classpath:/threepids/email/unbind-notification.eml");
|
||||||
}
|
}
|
||||||
|
|
||||||
public EmailTemplateConfig build() {
|
public EmailTemplateConfig build() {
|
||||||
@@ -40,7 +40,7 @@ public class EmailTemplateConfig extends GenericTemplateConfig {
|
|||||||
log.info("Session:");
|
log.info("Session:");
|
||||||
log.info(" Validation: {}", getSession().getValidation());
|
log.info(" Validation: {}", getSession().getValidation());
|
||||||
log.info(" Unbind:");
|
log.info(" Unbind:");
|
||||||
log.info(" Fraudulent: {}", getSession().getUnbind().getFraudulent());
|
log.info(" Notification: {}", getSession().getUnbind().getNotification());
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@@ -41,16 +41,25 @@ public class GenericTemplateConfig {
|
|||||||
|
|
||||||
public static class SessionUnbind {
|
public static class SessionUnbind {
|
||||||
|
|
||||||
private String fraudulent;
|
private String validation;
|
||||||
|
|
||||||
public String getFraudulent() {
|
private String notification;
|
||||||
return fraudulent;
|
|
||||||
|
public String getValidation() {
|
||||||
|
return validation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFraudulent(String fraudulent) {
|
public void setValidation(String validation) {
|
||||||
this.fraudulent = fraudulent;
|
this.validation = validation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getNotification() {
|
||||||
|
return notification;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNotification(String notification) {
|
||||||
|
this.notification = notification;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String validation;
|
private String validation;
|
||||||
|
@@ -30,7 +30,8 @@ public class PhoneSmsTemplateConfig extends GenericTemplateConfig {
|
|||||||
public PhoneSmsTemplateConfig() {
|
public PhoneSmsTemplateConfig() {
|
||||||
setInvite("classpath:/threepids/sms/invite-template.txt");
|
setInvite("classpath:/threepids/sms/invite-template.txt");
|
||||||
getSession().setValidation("classpath:/threepids/sms/validate-template.txt");
|
getSession().setValidation("classpath:/threepids/sms/validate-template.txt");
|
||||||
getSession().getUnbind().setFraudulent("classpath:/threepids/sms/unbind-fraudulent.txt");
|
getSession().getUnbind().setValidation("classpath:/threepids/sms/unbind-validation.txt");
|
||||||
|
getSession().getUnbind().setNotification("classpath:/threepids/sms/unbind-notification.txt");
|
||||||
}
|
}
|
||||||
|
|
||||||
public PhoneSmsTemplateConfig build() {
|
public PhoneSmsTemplateConfig build() {
|
||||||
@@ -39,7 +40,8 @@ public class PhoneSmsTemplateConfig extends GenericTemplateConfig {
|
|||||||
log.info("Session:");
|
log.info("Session:");
|
||||||
log.info(" Validation: {}", getSession().getValidation());
|
log.info(" Validation: {}", getSession().getValidation());
|
||||||
log.info(" Unbind:");
|
log.info(" Unbind:");
|
||||||
log.info(" Fraudulent: {}", getSession().getUnbind().getFraudulent());
|
log.info(" Validation: {}", getSession().getUnbind().getValidation());
|
||||||
|
log.info(" Notification: {}", getSession().getUnbind().getNotification());
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@@ -58,5 +58,4 @@ public class CryptoFactory {
|
|||||||
public static SignatureManager getSignatureManager(MxisdConfig cfg, Ed25519KeyManager keyMgr) {
|
public static SignatureManager getSignatureManager(MxisdConfig cfg, Ed25519KeyManager keyMgr) {
|
||||||
return new Ed25519SignatureManager(cfg, keyMgr);
|
return new Ed25519SignatureManager(cfg, keyMgr);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -26,6 +26,7 @@ import io.kamax.matrix.event.EventKey;
|
|||||||
import io.kamax.matrix.json.MatrixJson;
|
import io.kamax.matrix.json.MatrixJson;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.PublicKey;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public interface SignatureManager {
|
public interface SignatureManager {
|
||||||
@@ -106,4 +107,13 @@ public interface SignatureManager {
|
|||||||
*/
|
*/
|
||||||
Signature sign(byte[] data);
|
Signature sign(byte[] data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify the data.
|
||||||
|
*
|
||||||
|
* @param publicKey public key to verify
|
||||||
|
* @param signature signature to verify
|
||||||
|
* @param data the data to verify
|
||||||
|
* @return {@code true} if signature is valid, else {@code false}
|
||||||
|
*/
|
||||||
|
boolean verify(PublicKey publicKey, String signature, byte[] data);
|
||||||
}
|
}
|
||||||
|
@@ -33,7 +33,9 @@ import net.i2p.crypto.eddsa.EdDSAEngine;
|
|||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.PublicKey;
|
||||||
import java.security.SignatureException;
|
import java.security.SignatureException;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
public class Ed25519SignatureManager implements SignatureManager {
|
public class Ed25519SignatureManager implements SignatureManager {
|
||||||
|
|
||||||
@@ -92,4 +94,15 @@ public class Ed25519SignatureManager implements SignatureManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verify(PublicKey publicKey, String signature, byte[] data) {
|
||||||
|
try {
|
||||||
|
EdDSAEngine signEngine = new EdDSAEngine(MessageDigest.getInstance(keyMgr.getKeySpecs().getHashAlgorithm()));
|
||||||
|
signEngine.initVerify(publicKey);
|
||||||
|
signEngine.update(data);
|
||||||
|
return signEngine.verify(Base64.getDecoder().decode(signature));
|
||||||
|
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,13 +21,10 @@
|
|||||||
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import io.kamax.mxisd.exception.BadRequestException;
|
|
||||||
import io.kamax.mxisd.exception.NotAllowedException;
|
|
||||||
import io.kamax.mxisd.http.IsAPIv1;
|
import io.kamax.mxisd.http.IsAPIv1;
|
||||||
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
|
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
|
||||||
import io.kamax.mxisd.session.SessionManager;
|
import io.kamax.mxisd.session.SessionManager;
|
||||||
import io.undertow.server.HttpServerExchange;
|
import io.undertow.server.HttpServerExchange;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -46,20 +43,9 @@ public class SessionTpidUnbindHandler extends BasicHttpHandler {
|
|||||||
@Override
|
@Override
|
||||||
public void handleRequest(HttpServerExchange exchange) {
|
public void handleRequest(HttpServerExchange exchange) {
|
||||||
String auth = exchange.getRequestHeaders().getFirst("Authorization");
|
String auth = exchange.getRequestHeaders().getFirst("Authorization");
|
||||||
if (StringUtils.isNotEmpty(auth)) {
|
|
||||||
// We have a auth header to process
|
|
||||||
if (StringUtils.startsWith(auth, "X-Matrix ")) {
|
|
||||||
log.warn("A remote host attempted to unbind without proper authorization. Request was denied");
|
|
||||||
log.warn("See https://github.com/kamax-matrix/mxisd/wiki/mxisd-and-your-privacy for more info");
|
|
||||||
throw new NotAllowedException("3PID can only be removed via 3PID sessions, not via Homeserver signature");
|
|
||||||
} else {
|
|
||||||
throw new BadRequestException("Illegal authorization type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonObject body = parseJsonObject(exchange);
|
JsonObject body = parseJsonObject(exchange);
|
||||||
sessionMgr.unbind(body);
|
sessionMgr.unbind(auth, body);
|
||||||
writeBodyAsUtf8(exchange, "{}");
|
writeBodyAsUtf8(exchange, "{}");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -37,6 +37,6 @@ public interface NotificationHandler {
|
|||||||
|
|
||||||
void sendForValidation(IThreePidSession session);
|
void sendForValidation(IThreePidSession session);
|
||||||
|
|
||||||
void sendForFraudulentUnbind(ThreePid tpid);
|
void sendForUnbind(ThreePid tpid);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -78,8 +78,8 @@ public class NotificationManager {
|
|||||||
ensureMedium(session.getThreePid().getMedium()).sendForValidation(session);
|
ensureMedium(session.getThreePid().getMedium()).sendForValidation(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendForFraudulentUnbind(ThreePid tpid) throws NotImplementedException {
|
public void sendForUnbind(ThreePid tpid) throws NotImplementedException {
|
||||||
ensureMedium(tpid.getMedium()).sendForFraudulentUnbind(tpid);
|
ensureMedium(tpid.getMedium()).sendForUnbind(tpid);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -20,52 +20,73 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.session;
|
package io.kamax.mxisd.session;
|
||||||
|
|
||||||
|
import static io.kamax.mxisd.config.SessionConfig.Policy.PolicyTemplate;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import io.kamax.matrix.MatrixID;
|
import io.kamax.matrix.MatrixID;
|
||||||
import io.kamax.matrix.ThreePid;
|
import io.kamax.matrix.ThreePid;
|
||||||
import io.kamax.matrix._MatrixID;
|
import io.kamax.matrix._MatrixID;
|
||||||
import io.kamax.matrix.json.GsonUtil;
|
import io.kamax.matrix.json.GsonUtil;
|
||||||
import io.kamax.mxisd.config.MatrixConfig;
|
import io.kamax.matrix.json.MatrixJson;
|
||||||
import io.kamax.mxisd.config.SessionConfig;
|
import io.kamax.mxisd.config.MxisdConfig;
|
||||||
|
import io.kamax.mxisd.crypto.SignatureManager;
|
||||||
import io.kamax.mxisd.exception.BadRequestException;
|
import io.kamax.mxisd.exception.BadRequestException;
|
||||||
import io.kamax.mxisd.exception.NotAllowedException;
|
import io.kamax.mxisd.exception.NotAllowedException;
|
||||||
|
import io.kamax.mxisd.exception.RemoteHomeServerException;
|
||||||
import io.kamax.mxisd.exception.SessionNotValidatedException;
|
import io.kamax.mxisd.exception.SessionNotValidatedException;
|
||||||
import io.kamax.mxisd.exception.SessionUnknownException;
|
import io.kamax.mxisd.exception.SessionUnknownException;
|
||||||
import io.kamax.mxisd.lookup.SingleLookupReply;
|
import io.kamax.mxisd.lookup.SingleLookupReply;
|
||||||
import io.kamax.mxisd.lookup.SingleLookupRequest;
|
import io.kamax.mxisd.lookup.SingleLookupRequest;
|
||||||
import io.kamax.mxisd.lookup.ThreePidValidation;
|
import io.kamax.mxisd.lookup.ThreePidValidation;
|
||||||
|
import io.kamax.mxisd.matrix.HomeserverFederationResolver;
|
||||||
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 net.i2p.crypto.eddsa.EdDSAPublicKey;
|
||||||
|
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
|
||||||
|
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
|
||||||
|
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang3.RandomStringUtils;
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Calendar;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import static io.kamax.mxisd.config.SessionConfig.Policy.PolicyTemplate;
|
|
||||||
|
|
||||||
public class SessionManager {
|
public class SessionManager {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(SessionManager.class);
|
private static final Logger log = LoggerFactory.getLogger(SessionManager.class);
|
||||||
|
|
||||||
private SessionConfig cfg;
|
private MxisdConfig cfg;
|
||||||
private MatrixConfig mxCfg;
|
|
||||||
private IStorage storage;
|
private IStorage storage;
|
||||||
private NotificationManager notifMgr;
|
private NotificationManager notifMgr;
|
||||||
|
private HomeserverFederationResolver resolver;
|
||||||
|
private CloseableHttpClient client;
|
||||||
|
private SignatureManager signatureManager;
|
||||||
|
|
||||||
public SessionManager(
|
public SessionManager(
|
||||||
SessionConfig cfg,
|
MxisdConfig cfg,
|
||||||
MatrixConfig mxCfg,
|
|
||||||
IStorage storage,
|
IStorage storage,
|
||||||
NotificationManager notifMgr
|
NotificationManager notifMgr,
|
||||||
|
HomeserverFederationResolver resolver,
|
||||||
|
CloseableHttpClient client,
|
||||||
|
SignatureManager signatureManager
|
||||||
) {
|
) {
|
||||||
this.cfg = cfg;
|
this.cfg = cfg;
|
||||||
this.mxCfg = mxCfg;
|
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
this.notifMgr = notifMgr;
|
this.notifMgr = notifMgr;
|
||||||
|
this.resolver = resolver;
|
||||||
|
this.client = client;
|
||||||
|
this.signatureManager = signatureManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ThreePidSession getSession(String sid, String secret) {
|
private ThreePidSession getSession(String sid, String secret) {
|
||||||
@@ -86,7 +107,7 @@ public class SessionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
PolicyTemplate policy = cfg.getPolicy().getValidation();
|
PolicyTemplate policy = cfg.getSession().getPolicy().getValidation();
|
||||||
if (!policy.isEnabled()) {
|
if (!policy.isEnabled()) {
|
||||||
throw new NotAllowedException("Validating 3PID is disabled");
|
throw new NotAllowedException("Validating 3PID is disabled");
|
||||||
}
|
}
|
||||||
@@ -98,7 +119,8 @@ public class SessionManager {
|
|||||||
ThreePidSession session = new ThreePidSession(dao.get());
|
ThreePidSession session = new ThreePidSession(dao.get());
|
||||||
log.info("We already have a session for {}: {}", tpid, session.getId());
|
log.info("We already have a session for {}: {}", tpid, session.getId());
|
||||||
if (session.getAttempt() < attempt) {
|
if (session.getAttempt() < attempt) {
|
||||||
log.info("Received attempt {} is greater than stored attempt {}, sending validation communication", attempt, session.getAttempt());
|
log.info("Received attempt {} is greater than stored attempt {}, sending validation communication", attempt,
|
||||||
|
session.getAttempt());
|
||||||
notifMgr.sendForValidation(session);
|
notifMgr.sendForValidation(session);
|
||||||
log.info("Sent validation notification to {}", tpid);
|
log.info("Sent validation notification to {}", tpid);
|
||||||
session.increaseAttempt();
|
session.increaseAttempt();
|
||||||
@@ -161,8 +183,9 @@ public class SessionManager {
|
|||||||
_MatrixID mxid = MatrixID.asAcceptable(mxidRaw);
|
_MatrixID mxid = MatrixID.asAcceptable(mxidRaw);
|
||||||
|
|
||||||
// Only accept binds if the domain matches our own
|
// Only accept binds if the domain matches our own
|
||||||
if (!StringUtils.equalsIgnoreCase(mxCfg.getDomain(), mxid.getDomain())) {
|
final String domain = cfg.getMatrix().getDomain();
|
||||||
throw new NotAllowedException("Only Matrix IDs from domain " + mxCfg.getDomain() + " can be bound");
|
if (!StringUtils.equalsIgnoreCase(domain, mxid.getDomain())) {
|
||||||
|
throw new NotAllowedException("Only Matrix IDs from domain " + domain + " can be bound");
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Session {}: Binding of {}:{} to Matrix ID {} is accepted",
|
log.info("Session {}: Binding of {}:{} to Matrix ID {} is accepted",
|
||||||
@@ -174,7 +197,12 @@ public class SessionManager {
|
|||||||
return new SingleLookupReply(request, mxid);
|
return new SingleLookupReply(request, mxid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unbind(JsonObject reqData) {
|
public void unbind(String auth, JsonObject reqData) {
|
||||||
|
if (!cfg.getSession().getPolicy().getUnbind().getEnabled()) {
|
||||||
|
log.error("Unbind disabled.");
|
||||||
|
throw new NotAllowedException("Unbinding 3PID is disabled");
|
||||||
|
}
|
||||||
|
|
||||||
_MatrixID mxid;
|
_MatrixID mxid;
|
||||||
try {
|
try {
|
||||||
mxid = MatrixID.asAcceptable(GsonUtil.getStringOrThrow(reqData, "mxid"));
|
mxid = MatrixID.asAcceptable(GsonUtil.getStringOrThrow(reqData, "mxid"));
|
||||||
@@ -186,6 +214,133 @@ public class SessionManager {
|
|||||||
String secret = GsonUtil.getStringOrNull(reqData, "client_secret");
|
String secret = GsonUtil.getStringOrNull(reqData, "client_secret");
|
||||||
ThreePid tpid = GsonUtil.get().fromJson(GsonUtil.getObj(reqData, "threepid"), ThreePid.class);
|
ThreePid tpid = GsonUtil.get().fromJson(GsonUtil.getObj(reqData, "threepid"), ThreePid.class);
|
||||||
|
|
||||||
|
if (tpid == null || StringUtils.isBlank(tpid.getAddress()) || StringUtils.isBlank(tpid.getMedium())) {
|
||||||
|
throw new BadRequestException("Missing required 3PID");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(sid) && StringUtils.isNotBlank(secret)) {
|
||||||
|
checkSession(sid, secret, tpid, mxid);
|
||||||
|
} else if (StringUtils.isNotBlank(auth)) {
|
||||||
|
checkAuthorization(auth, reqData);
|
||||||
|
} else {
|
||||||
|
throw new NotAllowedException("Unable to validate request");
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Unbinding of {} {} to {} is accepted", tpid.getMedium(), tpid.getAddress(), mxid.getId());
|
||||||
|
notifMgr.sendForUnbind(tpid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkAuthorization(String auth, JsonObject reqData) {
|
||||||
|
if (!auth.startsWith("X-Matrix ")) {
|
||||||
|
throw new NotAllowedException("Wrong authorization header");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(cfg.getServer().getPublicUrl())) {
|
||||||
|
throw new NotAllowedException("Unable to verify request, missing `server.publicUrl` property");
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] params = auth.substring("X-Matrix ".length()).split(",");
|
||||||
|
|
||||||
|
String origin = null;
|
||||||
|
String key = null;
|
||||||
|
String sig = null;
|
||||||
|
for (String param : params) {
|
||||||
|
String[] paramItems = param.split("=");
|
||||||
|
String paramKey = paramItems[0];
|
||||||
|
String paramValue = paramItems[1];
|
||||||
|
switch (paramKey) {
|
||||||
|
case "origin":
|
||||||
|
origin = removeQuotes(paramValue);
|
||||||
|
break;
|
||||||
|
case "key":
|
||||||
|
key = removeQuotes(paramValue);
|
||||||
|
break;
|
||||||
|
case "sig":
|
||||||
|
sig = removeQuotes(paramValue);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log.error("Unknown parameter: {}", param);
|
||||||
|
throw new BadRequestException("Authorization with unknown parameter");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(origin) || StringUtils.isBlank(key) || StringUtils.isBlank(sig)) {
|
||||||
|
log.error("Missing required parameters");
|
||||||
|
throw new BadRequestException("Missing required header parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObject jsonObject = new JsonObject();
|
||||||
|
jsonObject.addProperty("method", "POST");
|
||||||
|
jsonObject.addProperty("uri", "/_matrix/identity/api/v1/3pid/unbind");
|
||||||
|
jsonObject.addProperty("origin", origin);
|
||||||
|
jsonObject.addProperty("destination_is", cfg.getServer().getPublicUrl());
|
||||||
|
jsonObject.add("content", reqData);
|
||||||
|
|
||||||
|
String canonical = MatrixJson.encodeCanonical(jsonObject);
|
||||||
|
|
||||||
|
String originUrl = resolver.resolve(origin).toString();
|
||||||
|
|
||||||
|
validateServerKey(key, sig, canonical, originUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String removeQuotes(String origin) {
|
||||||
|
return origin.startsWith("\"") && origin.endsWith("\"") ? origin.substring(1, origin.length() - 1) : origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateServerKey(String key, String signature, String canonical, String originUrl) {
|
||||||
|
HttpGet request = new HttpGet(originUrl + "/_matrix/key/v2/server");
|
||||||
|
log.info("Get keys from the server {}", request.getURI());
|
||||||
|
try (CloseableHttpResponse response = client.execute(request)) {
|
||||||
|
int statusCode = response.getStatusLine().getStatusCode();
|
||||||
|
log.info("Answer code: {}", statusCode);
|
||||||
|
if (statusCode == 200) {
|
||||||
|
verifyKey(key, signature, canonical, response);
|
||||||
|
} else {
|
||||||
|
throw new RemoteHomeServerException("Unable to fetch server keys.");
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
String message = "Unable to get server keys: " + originUrl;
|
||||||
|
log.error(message, e);
|
||||||
|
throw new IllegalArgumentException(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyKey(String key, String signature, String canonical, CloseableHttpResponse response) throws IOException {
|
||||||
|
final String content = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||||
|
log.info("Answer body: {}", content);
|
||||||
|
final JsonObject responseObject = GsonUtil.parseObj(content);
|
||||||
|
final long validUntilTs = GsonUtil.getLong(responseObject, "valid_until_ts");
|
||||||
|
|
||||||
|
final Calendar calendar = Calendar.getInstance();
|
||||||
|
calendar.setTimeInMillis(validUntilTs);
|
||||||
|
if (calendar.before(Calendar.getInstance())) {
|
||||||
|
final String msg = "Key is expired";
|
||||||
|
log.error(msg);
|
||||||
|
throw new RemoteHomeServerException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
final JsonObject verifyKeys = GsonUtil.getObj(responseObject, "verify_keys");
|
||||||
|
final JsonObject keyObject = GsonUtil.getObj(verifyKeys, key);
|
||||||
|
final String publicKey = GsonUtil.getStringOrNull(keyObject, "key");
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(publicKey)) {
|
||||||
|
throw new RemoteHomeServerException("Missing server key.");
|
||||||
|
}
|
||||||
|
|
||||||
|
EdDSANamedCurveSpec ed25519CurveSpec = EdDSANamedCurveTable.ED_25519_CURVE_SPEC;
|
||||||
|
EdDSAPublicKeySpec publicKeySpec = new EdDSAPublicKeySpec(Base64.getDecoder().decode(publicKey), ed25519CurveSpec);
|
||||||
|
EdDSAPublicKey dsaPublicKey = new EdDSAPublicKey(publicKeySpec);
|
||||||
|
|
||||||
|
final boolean verificationResult = signatureManager.verify(dsaPublicKey, signature, canonical.getBytes(StandardCharsets.UTF_8));
|
||||||
|
log.info("Verification result: {}", verificationResult);
|
||||||
|
if (!verificationResult) {
|
||||||
|
throw new RemoteHomeServerException("Unable to verify request.");
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Request was authorized.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkSession(String sid, String secret, ThreePid tpid, _MatrixID mxid) {
|
||||||
// We ensure the session was validated
|
// We ensure the session was validated
|
||||||
ThreePidSession session = getSessionIfValidated(sid, secret);
|
ThreePidSession session = getSessionIfValidated(sid, secret);
|
||||||
|
|
||||||
@@ -195,12 +350,11 @@ public class SessionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We only allow unbind for the domain we manage, mirroring bind
|
// We only allow unbind for the domain we manage, mirroring bind
|
||||||
if (!StringUtils.equalsIgnoreCase(mxCfg.getDomain(), mxid.getDomain())) {
|
final CharSequence domain = cfg.getMatrix().getDomain();
|
||||||
throw new NotAllowedException("Only Matrix IDs from domain " + mxCfg.getDomain() + " can be unbound");
|
if (!StringUtils.equalsIgnoreCase(domain, mxid.getDomain())) {
|
||||||
|
throw new NotAllowedException("Only Matrix IDs from domain " + domain + " can be unbound");
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Session {}: Unbinding of {}:{} to Matrix ID {} is accepted",
|
log.info("Request was authorized.");
|
||||||
session.getId(), session.getThreePid().getMedium(), session.getThreePid().getAddress(), mxid.getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -115,7 +115,7 @@ public class EmailSmtpConnector implements EmailConnector {
|
|||||||
msg.setRecipients(Message.RecipientType.TO, recipient);
|
msg.setRecipients(Message.RecipientType.TO, recipient);
|
||||||
msg.saveChanges();
|
msg.saveChanges();
|
||||||
|
|
||||||
log.info("Sending invite to {} via SMTP using {}:{}", recipient, cfg.getHost(), cfg.getPort());
|
log.info("Sending email to {} via SMTP using {}:{}", recipient, cfg.getHost(), cfg.getPort());
|
||||||
SMTPTransport transport = (SMTPTransport) session.getTransport("smtp");
|
SMTPTransport transport = (SMTPTransport) session.getTransport("smtp");
|
||||||
|
|
||||||
if (cfg.getTls() < 3) {
|
if (cfg.getTls() < 3) {
|
||||||
@@ -134,12 +134,12 @@ public class EmailSmtpConnector implements EmailConnector {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
transport.sendMessage(msg, InternetAddress.parse(recipient));
|
transport.sendMessage(msg, InternetAddress.parse(recipient));
|
||||||
log.info("Invite to {} was sent", recipient);
|
log.info("Email to {} was sent", recipient);
|
||||||
} finally {
|
} finally {
|
||||||
transport.close();
|
transport.close();
|
||||||
}
|
}
|
||||||
} catch (UnsupportedEncodingException | MessagingException e) {
|
} catch (UnsupportedEncodingException | MessagingException e) {
|
||||||
throw new RuntimeException("Unable to send e-mail invite to " + recipient, e);
|
throw new RuntimeException("Unable to send e-mail to " + recipient, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -79,9 +79,9 @@ public abstract class GenericTemplateNotificationGenerator extends PlaceholderNo
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getForFraudulentUnbind(ThreePid tpid) {
|
public String getForNotificationUnbind(ThreePid tpid) {
|
||||||
log.info("Generating notification content for fraudulent unbind");
|
log.info("Generating notification content for unbind");
|
||||||
return populateForFraudulentUndind(tpid, getTemplateContent(cfg.getSession().getUnbind().getFraudulent()));
|
return populateForNotificationUndind(tpid, getTemplateContent(cfg.getSession().getUnbind().getNotification()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -37,6 +37,6 @@ public interface NotificationGenerator {
|
|||||||
|
|
||||||
String getForValidation(IThreePidSession session);
|
String getForValidation(IThreePidSession session);
|
||||||
|
|
||||||
String getForFraudulentUnbind(ThreePid tpid);
|
String getForNotificationUnbind(ThreePid tpid);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -127,7 +127,7 @@ public abstract class PlaceholderNotificationGenerator {
|
|||||||
.replace("%NEXT_URL%", validationLink);
|
.replace("%NEXT_URL%", validationLink);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String populateForFraudulentUndind(ThreePid tpid, String input) {
|
protected String populateForNotificationUndind(ThreePid tpid, String input) {
|
||||||
return populateForCommon(tpid, input);
|
return populateForCommon(tpid, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -73,8 +73,8 @@ public abstract class GenericNotificationHandler<A extends ThreePidConnector, B
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendForFraudulentUnbind(ThreePid tpid) {
|
public void sendForUnbind(ThreePid tpid) {
|
||||||
send(connector, tpid.getAddress(), generator.getForFraudulentUnbind(tpid));
|
send(connector, tpid.getAddress(), generator.getForNotificationUnbind(tpid));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -129,10 +129,10 @@ public class EmailSendGridNotificationHandler extends PlaceholderNotificationGen
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendForFraudulentUnbind(ThreePid tpid) {
|
public void sendForUnbind(ThreePid tpid) {
|
||||||
EmailTemplate template = cfg.getTemplates().getSession().getUnbind().getFraudulent();
|
EmailTemplate template = cfg.getTemplates().getSession().getUnbind();
|
||||||
if (StringUtils.isAllBlank(template.getBody().getText(), template.getBody().getHtml())) {
|
if (StringUtils.isAllBlank(template.getBody().getText(), template.getBody().getHtml())) {
|
||||||
throw new FeatureNotAvailable("No template has been configured for fraudulent unbind notifications");
|
throw new FeatureNotAvailable("No template has been configured for unbind notifications");
|
||||||
}
|
}
|
||||||
|
|
||||||
Email email = getEmail();
|
Email email = getEmail();
|
||||||
|
@@ -1,135 +0,0 @@
|
|||||||
Subject: IMPORTANT - %DOMAIN% Matrix Identity Server - Unauthorized 3PID unbind blocked
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: multipart/alternative;
|
|
||||||
boundary="7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ"
|
|
||||||
|
|
||||||
--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ
|
|
||||||
Content-Type: text/plain; charset=UTF-8
|
|
||||||
Content-Disposition: inline
|
|
||||||
|
|
||||||
Hi,
|
|
||||||
|
|
||||||
**THIS IS IMPORTANT, PLEASE READ CAREFULLY**.
|
|
||||||
If you are the system administrator of the Matrix installation, read the second section.
|
|
||||||
|
|
||||||
This is a notification email that a possibly unauthorized entity has attempted to alter your
|
|
||||||
3PIDs (email, phone numbers, etc.) settings. The request was denied and no change has been made.
|
|
||||||
|
|
||||||
This is so you are aware of a possible failure in case you just tried to remove a 3PID from your account.
|
|
||||||
|
|
||||||
If you do not understand this email, please forward it to your System administrator.
|
|
||||||
|
|
||||||
-----------
|
|
||||||
|
|
||||||
As the system administrator:
|
|
||||||
|
|
||||||
If you are using synapse as a Homeserver, this is a known issue related to MSC1194 [1] and abuse of separation of concerns.
|
|
||||||
As a privacy-centric product and to protect your privacy, the request was actively blocked. We have written a more detailed
|
|
||||||
explanation on our Privacy wiki page [2] (Direct link [3]) so you can fully grasp the impact for you and your users.
|
|
||||||
|
|
||||||
We have open an issue [4] on the synapse repos to reflect the related privacy concerns and GDPR violation(s) and would
|
|
||||||
appreciate if you could comment on it or simply adds a thumbs up so the concerns are finally dealt with by the synapse dev team.
|
|
||||||
|
|
||||||
If you are using another Homeserver or this came following no action from your own users, then you have been the target
|
|
||||||
of an unbind attack from a rogue entity which was blocked. You may want to check your logs to see the exact source of
|
|
||||||
the attack and take relevant actions following your policy.
|
|
||||||
|
|
||||||
If you would like to disable these notifications, please see the 3PID sessions configuration documentation [5].
|
|
||||||
|
|
||||||
Thanks,
|
|
||||||
|
|
||||||
%DOMAIN_PRETTY% Admins
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
[1] https://github.com/matrix-org/matrix-doc/issues/1194
|
|
||||||
[2] https://github.com/kamax-matrix/mxisd/wiki/mxisd-and-your-privacy
|
|
||||||
[3] https://github.com/kamax-matrix/mxisd/wiki/mxisd-and-your-privacy#msc1194-synapse-and-impacts-on-your-privacy
|
|
||||||
[4] https://github.com/matrix-org/synapse/issues/4540
|
|
||||||
[5] https://github.com/kamax-matrix/mxisd/blob/master/docs/threepids/session/session.md#configuration
|
|
||||||
|
|
||||||
--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;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<table id="page">
|
|
||||||
<tr>
|
|
||||||
<td> </td>
|
|
||||||
<td id="inner">
|
|
||||||
<p>Hi,</p>
|
|
||||||
|
|
||||||
<p><b>THIS IS IMPORTANT, PLEASE READ CAREFULLY</b>.<br/>
|
|
||||||
If you are the system administrator of the Matrix installation, read the second section.</p>
|
|
||||||
|
|
||||||
<p>This is a notification email that a possibly unauthorized entity has attempted to alter your
|
|
||||||
3PIDs (email, phone numbers, etc.) settings. The request was denied and no change has been made.</p>
|
|
||||||
|
|
||||||
<p>This is so you are aware of a possible failure in case you just tried to remove a 3PID from your account.</p>
|
|
||||||
|
|
||||||
<p>If you do not understand this email, please forward it to your System administrator.</p>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<p>As the system administrator:</p>
|
|
||||||
|
|
||||||
<p>If you are using synapse as a Homeserver, this is a known issue related to <a href="https://github.com/matrix-org/matrix-doc/issues/1194">MSC1194</a>
|
|
||||||
and abuse of separation of concerns. As a privacy-centric product and to protect your privacy, the request was actively
|
|
||||||
blocked. We have written a more detailed explanation on our <a href="https://github.com/kamax-matrix/mxisd/wiki/mxisd-and-your-privacy">Privacy wiki page</a>
|
|
||||||
(<a href="https://github.com/kamax-matrix/mxisd/wiki/mxisd-and-your-privacy#msc1194-synapse-and-impacts-on-your-privacy">Direct link to section</a>)
|
|
||||||
so you can fully grasp the impact for you and your users.</p>
|
|
||||||
|
|
||||||
<p>We have open an issue on the synapse repos to reflect the related privacy concerns and GDPR violation(s) and would
|
|
||||||
appreciate if you could comment on it or simply adds a thumbs up so the concerns are finally dealt with by the synapse dev team.<br/>
|
|
||||||
Issue: <a href="https://github.com/matrix-org/synapse/issues/4540">https://github.com/matrix-org/synapse/issues/4540</a></p>
|
|
||||||
|
|
||||||
<p>If you are using another Homeserver or this came following no action from your own users, then you have been the target
|
|
||||||
of an unbind attack from a rogue entity which was blocked. You may want to check your logs to see the exact source of
|
|
||||||
the attack and take relevant actions following your policy.</p>
|
|
||||||
|
|
||||||
<p>If you would like to disable these notifications, please see the
|
|
||||||
<a href="https://github.com/kamax-matrix/mxisd/blob/master/docs/threepids/session/session.md#configuration">3PID sessions configuration documentation.</a></p>
|
|
||||||
|
|
||||||
<p>Thanks,</p>
|
|
||||||
|
|
||||||
<p>%DOMAIN_PRETTY% Admins</p>
|
|
||||||
</td>
|
|
||||||
<td> </td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
--M3yzHl5YZehm9v4bAM8sKEdcOoVnRnKR--
|
|
||||||
|
|
||||||
--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ--
|
|
77
src/main/resources/threepids/email/unbind-notification.eml
Normal file
77
src/main/resources/threepids/email/unbind-notification.eml
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
Subject: Unbind 3PID
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: multipart/alternative;
|
||||||
|
boundary="7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ"
|
||||||
|
|
||||||
|
--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Disposition: inline
|
||||||
|
|
||||||
|
Hello there!
|
||||||
|
|
||||||
|
You or a server on your behalf unbinded your email.
|
||||||
|
|
||||||
|
If you didn't make this request, please contact the system administrator.
|
||||||
|
|
||||||
|
%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>You or a server on your behalf unbinded your email.</p>
|
||||||
|
|
||||||
|
<p>If you didn't make this request, please contact the system administrator.</p>
|
||||||
|
|
||||||
|
<p>%DOMAIN_PRETTY% Admins</p>
|
||||||
|
</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
--M3yzHl5YZehm9v4bAM8sKEdcOoVnRnKR--
|
||||||
|
|
||||||
|
--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ--
|
@@ -1 +0,0 @@
|
|||||||
INFORMATIONAL ONLY - Someone attempted to change your Matrix 3PIDs, with a potential data leak. Please contact your system administrator.
|
|
1
src/main/resources/threepids/sms/unbind-notification.txt
Normal file
1
src/main/resources/threepids/sms/unbind-notification.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Your Matrix 3PID was unbinded.
|
Reference in New Issue
Block a user