Compare commits

...

8 Commits

Author SHA1 Message Date
Max Dor
92f10347d1 Fix #123 2019-05-30 14:18:11 +02:00
Max Dor
0298f66212 Fix #128 2019-05-30 13:58:40 +02:00
Max Dor
0ddd086bda Fix response body of /3pid/bind to match spec
- synapse did not check/validate the response as per spec until 0.99.5 it seems
- mxisd was never compliant also
2019-05-30 13:26:38 +02:00
Max Dor
544f8e59f0 Add check for legality of the returned Matrix ID in Auth
- Helps troubleshoot reported issues that might not be obvious at first
- Add basic unit test for auth manager
2019-05-28 19:28:46 +02:00
Max Dor
917f87bf8c Fix broken HTML tag in 3PID template 2019-05-28 16:01:01 +02:00
Max Dor
774795c203 Fix various logging/variable scopes 2019-05-27 17:12:52 +02:00
Max Dor
27b2976e42 Provide URL encoded placeholders in notification template for 3PID data 2019-05-18 02:20:13 +02:00
Max Dor
f16f184253 Minor internal changes
- Fix log statement to include expected value
- Change access level to method
2019-05-18 01:57:40 +02:00
28 changed files with 253 additions and 80 deletions

View File

@@ -19,29 +19,33 @@ All placeholders **MUST** be surrounded with `%` in the template. Per example, t
### Global
The following placeholders are available in every template:
| Placeholder | Purpose |
|---------------------|------------------------------------------------------------------------------|
| `DOMAIN` | Identity server authoritative domain, as configured in `matrix.domain` |
| `DOMAIN_PRETTY` | Same as `DOMAIN` with the first letter upper case and all other lower case |
| `FROM_EMAIL` | Email address configured in `threepid.medium.<3PID medium>.identity.from` |
| `FROM_NAME` | Name configured in `threepid.medium.<3PID medium>.identity.name` |
| `RECIPIENT_MEDIUM` | The 3PID medium, like `email` or `msisdn` |
| `RECIPIENT_ADDRESS` | The address to which the notification is sent |
| Placeholder | Purpose |
|---------------------------------|------------------------------------------------------------------------------|
| `DOMAIN` | Identity server authoritative domain, as configured in `matrix.domain` |
| `DOMAIN_PRETTY` | Same as `DOMAIN` with the first letter upper case and all other lower case |
| `FROM_EMAIL` | Email address configured in `threepid.medium.<3PID medium>.identity.from` |
| `FROM_NAME` | Name configured in `threepid.medium.<3PID medium>.identity.name` |
| `RECIPIENT_MEDIUM` | The 3PID medium, like `email` or `msisdn` |
| `RECIPIENT_MEDIUM_URL_ENCODED` | URL encoded value of `RECIPIENT_MEDIUM` |
| `RECIPIENT_ADDRESS` | The address to which the notification is sent |
| `RECIPIENT_ADDRESS_URL_ENCODED` | URL encoded value of `RECIPIENT_ADDRESS` |
### Room invitation
Specific placeholders:
| Placeholder | Purpose |
|---------------------|------------------------------------------------------------------------------------------|
| `SENDER_ID` | Matrix ID of the user who made the invite |
| `SENDER_NAME` | Display name of the user who made the invite, if not available/set, empty |
| `SENDER_NAME_OR_ID` | Display name of the user who made the invite. If not available/set, its Matrix ID |
| `INVITE_MEDIUM` | The 3PID medium for the invite. |
| `INVITE_ADDRESS` | The 3PID address for the invite. |
| `ROOM_ID` | The Matrix ID of the Room in which the invite took place |
| `ROOM_NAME` | The Name of the room in which the invite took place. If not available/set, empty |
| `ROOM_NAME_OR_ID` | The Name of the room in which the invite took place. If not available/set, its Matrix ID |
| `REGISTER_URL` | The URL to provide to the user allowing them to register their account, if needed |
| Placeholder | Purpose |
|------------------------------|-----------------------------------------------------------------------------------|
| `SENDER_ID` | Matrix ID of the user who made the invite |
| `SENDER_NAME` | Display name of the user who made the invite, if not available/set, empty |
| `SENDER_NAME_OR_ID` | Display name of the user who made the invite. If not available/set, its Matrix ID |
| `INVITE_MEDIUM` | The 3PID medium for the invite. |
| `INVITE_MEDIUM_URL_ENCODED` | URL encoded value of `INVITE_MEDIUM` |
| `INVITE_ADDRESS` | The 3PID address for the invite. |
| `INVITE_ADDRESS_URL_ENCODED` | URL encoded value of `INVITE_ADDRESS` |
| `ROOM_ID` | The Matrix ID of the Room in which the invite took place |
| `ROOM_NAME` | The Name of the room in which the invite took place. If not available/set, empty |
| `ROOM_NAME_OR_ID` | The Name of the room in which the invite took place. If not available/set, its ID |
| `REGISTER_URL` | The URL to provide to the user allowing them to register their account, if needed |
### Validation of 3PID Session
Specific placeholders:

View File

@@ -105,7 +105,7 @@ public class HttpMxisd {
.get(SessionValidateHandler.Path, SaneHandler.around(new SessionValidationGetHandler(m.getSession(), m.getConfig())))
.post(SessionValidateHandler.Path, SaneHandler.around(new SessionValidationPostHandler(m.getSession())))
.get(SessionTpidGetValidatedHandler.Path, SaneHandler.around(new SessionTpidGetValidatedHandler(m.getSession())))
.post(SessionTpidBindHandler.Path, SaneHandler.around(new SessionTpidBindHandler(m.getSession(), m.getInvite())))
.post(SessionTpidBindHandler.Path, SaneHandler.around(new SessionTpidBindHandler(m.getSession(), m.getInvite(), m.getSign())))
.post(SessionTpidUnbindHandler.Path, SaneHandler.around(new SessionTpidUnbindHandler(m.getSession())))
.post(SignEd25519Handler.Path, SaneHandler.around(new SignEd25519Handler(m.getConfig(), m.getInvite(), m.getSign())))

View File

@@ -107,7 +107,7 @@ public class Mxisd {
store = new OrmLiteSqlStorage(cfg);
keyMgr = CryptoFactory.getKeyManager(cfg.getKey());
signMgr = CryptoFactory.getSignatureManager(keyMgr);
signMgr = CryptoFactory.getSignatureManager(cfg, keyMgr);
clientDns = new ClientDnsOverwrite(cfg.getDns().getOverwrite());
synapse = new Synapse(cfg.getSynapseSql());

View File

@@ -64,6 +64,8 @@ import java.util.Objects;
public class AuthManager {
private static final Logger log = LoggerFactory.getLogger(AuthManager.class);
private static final String TypeKey = "type";
private static final String UserKey = "user";
private static final String IdentifierKey = "identifier";
@@ -72,7 +74,6 @@ public class AuthManager {
private static final String UserIdTypeValue = "m.id.user";
private static final String ThreepidTypeValue = "m.id.thirdparty";
private transient final Logger log = LoggerFactory.getLogger(AuthManager.class);
private final Gson gson = GsonUtil.get(); // FIXME replace
private List<AuthenticatorProvider> providers;
@@ -138,6 +139,12 @@ public class AuthManager {
invMgr.publishMappingIfInvited(new ThreePidMapping(pid, mxId));
}
try {
MatrixID.asValid(mxId);
} catch (IllegalArgumentException e) {
log.warn("The returned User ID {} is not a valid Matrix ID. Login might fail at the Homeserver level", mxId);
}
invMgr.lookupMappingsForInvites();
return authResult;

View File

@@ -83,6 +83,12 @@ public class MxisdConfig {
}
public static MxisdConfig forDomain(String domain) {
MxisdConfig cfg = new MxisdConfig();
cfg.getMatrix().setDomain(domain);
return cfg;
}
private AppServiceConfig appsvc = new AppServiceConfig();
private AuthenticationConfig auth = new AuthenticationConfig();
private DirectoryConfig directory = new DirectoryConfig();
@@ -309,6 +315,13 @@ public class MxisdConfig {
this.wordpress = wordpress;
}
public MxisdConfig inMemory() {
getKey().setPath(":memory:");
getStorage().getProvider().getSqlite().setDatabase(":memory:");
return this;
}
public MxisdConfig build() {
if (StringUtils.isBlank(getServer().getName())) {
getServer().setName(getMatrix().getDomain());

View File

@@ -21,6 +21,7 @@
package io.kamax.mxisd.crypto;
import io.kamax.mxisd.config.KeyConfig;
import io.kamax.mxisd.config.MxisdConfig;
import io.kamax.mxisd.crypto.ed25519.Ed25519KeyManager;
import io.kamax.mxisd.crypto.ed25519.Ed25519SignatureManager;
import io.kamax.mxisd.storage.crypto.FileKeyStore;
@@ -54,8 +55,8 @@ public class CryptoFactory {
return new Ed25519KeyManager(store);
}
public static SignatureManager getSignatureManager(Ed25519KeyManager keyMgr) {
return new Ed25519SignatureManager(keyMgr);
public static SignatureManager getSignatureManager(MxisdConfig cfg, Ed25519KeyManager keyMgr) {
return new Ed25519SignatureManager(cfg, keyMgr);
}
}

View File

@@ -30,6 +30,18 @@ import java.util.Objects;
public interface SignatureManager {
/**
* Sign the message with the default domain and add the signature to the <code>signatures</code> key.
* <p>
* If the key does not exist yet, it is created. If the key exist, the produced signature will be merged with any
* existing ones.
*
* @param message The message to sign with the default domain and add the produced signature to
* @return The provided message with the new signature
* @throws IllegalArgumentException If the <code>signatures</code> key exists and its value is not a JSON object
*/
JsonObject signMessageGson(JsonObject message) throws IllegalArgumentException;
/**
* Sign the message and add the signature to the <code>signatures</code> key.
* <p>
@@ -39,7 +51,7 @@ public interface SignatureManager {
* @param domain The domain under which the signature should be added
* @param message The message to sign and add the produced signature to
* @return The provided message with the new signature
* @throws IllegalArgumentException If the <code>signatures</code> value is not a JSON object
* @throws IllegalArgumentException If the <code>signatures</code> key exists and its value is not a JSON object
*/
default JsonObject signMessageGson(String domain, JsonObject message) throws IllegalArgumentException {
JsonElement signEl = message.remove(EventKey.Signatures.get());

View File

@@ -23,6 +23,8 @@ package io.kamax.mxisd.crypto.ed25519;
import com.google.gson.JsonObject;
import io.kamax.matrix.codec.MxBase64;
import io.kamax.matrix.json.MatrixJson;
import io.kamax.mxisd.config.MxisdConfig;
import io.kamax.mxisd.config.ServerConfig;
import io.kamax.mxisd.crypto.KeyIdentifier;
import io.kamax.mxisd.crypto.Signature;
import io.kamax.mxisd.crypto.SignatureManager;
@@ -35,12 +37,19 @@ import java.security.SignatureException;
public class Ed25519SignatureManager implements SignatureManager {
private final ServerConfig cfg;
private final Ed25519KeyManager keyMgr;
public Ed25519SignatureManager(Ed25519KeyManager keyMgr) {
public Ed25519SignatureManager(MxisdConfig cfg, Ed25519KeyManager keyMgr) {
this.cfg = cfg.getServer();
this.keyMgr = keyMgr;
}
@Override
public JsonObject signMessageGson(JsonObject message) throws IllegalArgumentException {
return signMessageGson(cfg.getName(), message);
}
@Override
public JsonObject signMessageGson(String domain, String message) {
Signature sign = sign(message);

View File

@@ -33,8 +33,7 @@ public class InternalServerError extends HttpMatrixException {
super(
HttpStatus.SC_INTERNAL_SERVER_ERROR,
"M_UNKNOWN",
"An internal server error occured. If this error persists, please contact support with reference #" +
Instant.now().toEpochMilli()
"An internal server error occurred. Contact your administrator with reference Transaction #" + Instant.now().toEpochMilli()
);
}

View File

@@ -97,7 +97,7 @@ public class SaneHandler extends BasicHttpHandler {
if (StringUtils.isNotBlank(e.getInternalReason())) {
log.error("Transaction #{} - {}", e.getReference(), e.getInternalReason());
} else {
log.error("Transaction #{}", e);
log.error("Transaction #{}", e.getReference(), e);
}
handleException(exchange, e);

View File

@@ -36,7 +36,7 @@ public class RestAuthHandler extends BasicHttpHandler {
public static final String Path = "/_matrix-internal/identity/v1/check_credentials";
private transient final Logger log = LoggerFactory.getLogger(RestAuthHandler.class);
private static final Logger log = LoggerFactory.getLogger(RestAuthHandler.class);
private AuthManager mgr;
@@ -45,7 +45,7 @@ public class RestAuthHandler extends BasicHttpHandler {
}
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
public void handleRequest(HttpServerExchange exchange) {
JsonObject authData = parseJsonObject(exchange, "user");
if (!authData.has("id") || !authData.has("password")) {
throw new JsonMemberNotFoundException("Missing id or password keys");

View File

@@ -40,7 +40,7 @@ public class UserDirectorySearchHandler extends HomeserverProxyHandler {
}
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
public void handleRequest(HttpServerExchange exchange) {
String accessToken = getAccessToken(exchange);
UserDirectorySearchRequest searchQuery = parseJsonTo(exchange, UserDirectorySearchRequest.class);
URI target = URI.create(exchange.getRequestURL());

View File

@@ -37,7 +37,7 @@ public class BulkLookupHandler extends LookupHandler {
public static final String Path = IsAPIv1.Base + "/bulk_lookup";
private transient final Logger log = LoggerFactory.getLogger(SingleLookupHandler.class);
private static final Logger log = LoggerFactory.getLogger(SingleLookupHandler.class);
private LookupStrategy strategy;

View File

@@ -31,7 +31,7 @@ public class EphemeralKeyIsValidHandler extends KeyIsValidHandler {
public static final String Path = IsAPIv1.Base + "/pubkey/ephemeral/isvalid";
private transient final Logger log = LoggerFactory.getLogger(EphemeralKeyIsValidHandler.class);
private static final Logger log = LoggerFactory.getLogger(EphemeralKeyIsValidHandler.class);
private KeyManager mgr;

View File

@@ -21,11 +21,15 @@
package io.kamax.mxisd.http.undertow.handler.identity.v1;
import com.google.gson.JsonObject;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.crypto.SignatureManager;
import io.kamax.mxisd.exception.BadRequestException;
import io.kamax.mxisd.http.IsAPIv1;
import io.kamax.mxisd.http.io.identity.BindRequest;
import io.kamax.mxisd.http.io.identity.SingeLookupReplyJson;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.kamax.mxisd.invitation.InvitationManager;
import io.kamax.mxisd.lookup.SingleLookupReply;
import io.kamax.mxisd.session.SessionManager;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.QueryParameterUtils;
@@ -42,14 +46,16 @@ public class SessionTpidBindHandler extends BasicHttpHandler {
public static final String Path = IsAPIv1.Base + "/3pid/bind";
private transient final Logger log = LoggerFactory.getLogger(SessionTpidBindHandler.class);
private static final Logger log = LoggerFactory.getLogger(SessionTpidBindHandler.class);
private SessionManager mgr;
private InvitationManager invMgr;
private SignatureManager signMgr;
public SessionTpidBindHandler(SessionManager mgr, InvitationManager invMgr) {
public SessionTpidBindHandler(SessionManager mgr, InvitationManager invMgr, SignatureManager signMgr) {
this.mgr = mgr;
this.invMgr = invMgr;
this.signMgr = signMgr;
}
@Override
@@ -74,8 +80,9 @@ public class SessionTpidBindHandler extends BasicHttpHandler {
}
try {
mgr.bind(bindReq.getSid(), bindReq.getSecret(), bindReq.getUserId());
respond(exchange, new JsonObject());
SingleLookupReply lookup = mgr.bind(bindReq.getSid(), bindReq.getSecret(), bindReq.getUserId());
JsonObject response = signMgr.signMessageGson(GsonUtil.makeObj(new SingeLookupReplyJson(lookup)));
respond(exchange, response);
} catch (BadRequestException e) {
log.info("requested session was not validated");

View File

@@ -72,7 +72,7 @@ public class SingleLookupReply {
}
public SingleLookupReply(SingleLookupRequest request, _MatrixID mxid) {
this(request, mxid, Instant.now(), Instant.ofEpochMilli(0), Instant.ofEpochMilli(253402300799000L));
this(request, mxid, Instant.now(), Instant.now().minusSeconds(60), Instant.now().plusSeconds(5 * 60));
}
public SingleLookupReply(SingleLookupRequest request, _MatrixID mxid, Instant timestamp, Instant notBefore, Instant notAfter) {

View File

@@ -79,7 +79,7 @@ public class IdentityServerUtils {
JsonElement el = parser.parse(IOUtils.toString(res.getEntity().getContent(), StandardCharsets.UTF_8));
if (!el.isJsonObject()) {
log.debug("IS {} did not send back an empty JSON object as per spec, not a valid IS");
log.debug("IS {} did not send back an empty JSON object as per spec, not a valid IS", remote);
return false;
}
@@ -90,7 +90,7 @@ public class IdentityServerUtils {
}
}
public static String getSrvRecordName(String domain) {
private static String getSrvRecordName(String domain) {
return "_matrix-identity._tcp." + domain;
}

View File

@@ -32,6 +32,7 @@ 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.SingleLookupRequest;
import io.kamax.mxisd.lookup.ThreePidValidation;
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
import io.kamax.mxisd.notification.NotificationManager;
@@ -151,7 +152,7 @@ public class SessionManager {
return new ThreePidValidation(session.getThreePid(), session.getValidationTime());
}
public void bind(String sid, String secret, String mxidRaw) {
public SingleLookupReply 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");
@@ -165,11 +166,16 @@ public class SessionManager {
// 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");
throw new NotAllowedException("Only Matrix IDs from domain " + mxCfg.getDomain() + " can be bound");
}
log.info("Session {}: Binding of {}:{} to Matrix ID {} is accepted",
session.getId(), session.getThreePid().getMedium(), session.getThreePid().getAddress(), mxid.getId());
SingleLookupRequest request = new SingleLookupRequest();
request.setType(session.getThreePid().getMedium());
request.setThreePid(session.getThreePid().getAddress());
return new SingleLookupReply(request, mxid);
}
public void unbind(JsonObject reqData) {

View File

@@ -21,10 +21,12 @@
package io.kamax.mxisd.threepid.connector.phone;
import com.twilio.Twilio;
import com.twilio.exception.ApiException;
import com.twilio.rest.api.v2010.account.Message;
import com.twilio.type.PhoneNumber;
import io.kamax.mxisd.config.threepid.connector.PhoneTwilioConfig;
import io.kamax.mxisd.exception.BadRequestException;
import io.kamax.mxisd.exception.InternalServerError;
import io.kamax.mxisd.exception.NotImplementedException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -52,12 +54,17 @@ public class PhoneSmsTwilioConnector implements PhoneConnector {
@Override
public void send(String recipient, String content) {
if (StringUtils.isBlank(cfg.getAccountSid()) || StringUtils.isBlank(cfg.getAuthToken()) || StringUtils.isBlank(cfg.getNumber())) {
throw new BadRequestException("Phone numbers cannot be validated at this time. Contact your administrator.");
log.error("Twilio connector in not fully configured and is missing mandatory configuration values.");
throw new NotImplementedException("Phone numbers cannot be validated at this time. Contact your administrator.");
}
recipient = "+" + recipient;
log.info("Sending SMS notification from {} to {} with {} characters", cfg.getNumber(), recipient, content.length());
Message.creator(new PhoneNumber("+" + recipient), new PhoneNumber(cfg.getNumber()), content).create();
try {
Message.creator(new PhoneNumber("+" + recipient), new PhoneNumber(cfg.getNumber()), content).create();
} catch (ApiException e) {
throw new InternalServerError(e);
}
}
}

View File

@@ -27,6 +27,7 @@ import io.kamax.mxisd.http.IsAPIv1;
import io.kamax.mxisd.invitation.IMatrixIdInvite;
import io.kamax.mxisd.invitation.IThreePidInviteReply;
import io.kamax.mxisd.threepid.session.IThreePidSession;
import io.kamax.mxisd.util.RestClientUtils;
import org.apache.commons.lang.WordUtils;
import org.apache.commons.lang3.StringUtils;
@@ -56,7 +57,9 @@ public abstract class PlaceholderNotificationGenerator {
.replace("%DOMAIN%", mxCfg.getDomain())
.replace("%DOMAIN_PRETTY%", domainPretty)
.replace("%RECIPIENT_MEDIUM%", recipient.getMedium())
.replace("%RECIPIENT_ADDRESS%", recipient.getAddress());
.replace("%RECIPIENT_MEDIUM_URL_ENCODED%", RestClientUtils.urlEncode(recipient.getMedium()))
.replace("%RECIPIENT_ADDRESS%", recipient.getAddress())
.replace("%RECIPIENT_ADDRESS_URL_ENCODED%", RestClientUtils.urlEncode(recipient.getAddress()));
}
protected String populateForInvite(IMatrixIdInvite invite, String input) {
@@ -98,7 +101,9 @@ public abstract class PlaceholderNotificationGenerator {
.replace("%SENDER_NAME%", senderName)
.replace("%SENDER_NAME_OR_ID%", senderNameOrId)
.replace("%INVITE_MEDIUM%", tpid.getMedium())
.replace("%INVITE_MEDIUM_URL_ENCODED%", RestClientUtils.urlEncode(tpid.getMedium()))
.replace("%INVITE_ADDRESS%", tpid.getAddress())
.replace("%INVITE_ADDRESS_URL_ENCODED%", RestClientUtils.urlEncode(tpid.getAddress()))
.replace("%ROOM_ID%", invite.getInvite().getRoomId())
.replace("%ROOM_NAME%", roomName)
.replace("%ROOM_NAME_OR_ID%", roomNameOrId);

View File

@@ -25,12 +25,22 @@ import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
public class RestClientUtils {
private static Gson gson = GsonUtil.build();
public static String urlEncode(String value) {
try {
return URLEncoder.encode(value, StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
public static HttpPost post(String url, String body) {
StringEntity entity = new StringEntity(body, StandardCharsets.UTF_8);
entity.setContentType(ContentType.APPLICATION_JSON.toString());

View File

@@ -9,19 +9,12 @@ 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 %REGISTER_URL%
%SENDER_NAME_OR_ID% has invited you to the Matrix room [%ROOM_NAME_OR_ID%].
To join the conversation, register an account using %REGISTER_URL%
You may be required to provide the same email used for this invite during registration.
You can also register an account on a public server and get in touch with them.
About Matrix:
Matrix is an open standard for interoperable, decentralised, real-time communication
over IP, supporting group chat, file transfer, voice and video calling, integrations to
other apps, bridges to other communication systems and much more. It can be used to power
Instant Messaging, VoIP/WebRTC signalling, Internet of Things communication.
Thanks,
%DOMAIN_PRETTY% Admins
@@ -68,18 +61,11 @@ Content-Disposition: inline
<td id="inner">
<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="%REGISTER_URL%">%DOMAIN%</a>.</p>
<p>%SENDER_NAME_OR_ID% has invited you to the Matrix room [%ROOM_NAME_OR_ID%].<br/>
To join the conversation, register an account on <a href="%REGISTER_URL%">%DOMAIN%</a>.</p>
<pYou can also register an account on a public server and get in touch with them.</p>
<br>
<p>About Matrix:</p>
<p>Matrix is an open standard for interoperable, decentralised, real-time communication
over IP, supporting group chat, file transfer, voice and video calling, integrations to
other apps, bridges to other communication systems and much more. It can be used to power
Instant Messaging, VoIP/WebRTC signalling, Internet of Things communication.</p>
<p>You may be required to provide the same email used for this invite during registration.<br/>
You can also register an account on a public server and get in touch with them.</p>
<p>Thanks,</p>

View File

@@ -9,7 +9,7 @@ Content-Disposition: inline
Hi,
%SENDER_NAME_OR_ID% has invited you into a room [%ROOM_NAME_OR_ID%] on Matrix.
%SENDER_NAME_OR_ID% has invited you to the Matrix room [%ROOM_NAME_OR_ID%].
Thanks,
@@ -57,7 +57,7 @@ Content-Disposition: inline
<td id="inner">
<p>Hi,</p>
<p>%SENDER_NAME_OR_ID% has invited you into a room [%ROOM_NAME_OR_ID%] on Matrix.</p>
<p>%SENDER_NAME_OR_ID% has invited you to the Matrix room [%ROOM_NAME_OR_ID%].</p>
<p>Thanks,</p>

View File

@@ -33,11 +33,7 @@ public class MxisdDefaultTest {
@Test
public void defaultConfig() {
MxisdConfig cfg = new MxisdConfig();
cfg.getMatrix().setDomain(domain);
cfg.getKey().setPath(":memory:");
cfg.getStorage().getProvider().getSqlite().setDatabase(":memory:");
MxisdConfig cfg = MxisdConfig.forDomain(domain).inMemory();
Mxisd m = new Mxisd(cfg);
m.start();

View File

@@ -41,10 +41,7 @@ public class MxisdTest {
@Before
public void before() {
MxisdConfig cfg = new MxisdConfig();
cfg.getMatrix().setDomain("localhost");
cfg.getKey().setPath(":memory:");
cfg.getStorage().getProvider().getSqlite().setDatabase(":memory:");
MxisdConfig cfg = MxisdConfig.forDomain("localhost").inMemory();
MemoryThreePid mem3pid = new MemoryThreePid();
mem3pid.setMedium("email");

View File

@@ -0,0 +1,77 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2019 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.test.auth;
import io.kamax.mxisd.Mxisd;
import io.kamax.mxisd.auth.AuthManager;
import io.kamax.mxisd.auth.UserAuthResult;
import io.kamax.mxisd.config.MxisdConfig;
import io.kamax.mxisd.config.memory.MemoryIdentityConfig;
import io.kamax.mxisd.config.memory.MemoryThreePid;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
public class AuthManagerTest {
private static AuthManager mgr;
// FIXME we should be able to easily build the class ourselves
// FIXME use constants
@BeforeClass
public static void beforeClass() {
MxisdConfig cfg = new MxisdConfig();
cfg.getMatrix().setDomain("localhost");
cfg.getKey().setPath(":memory:");
cfg.getStorage().getProvider().getSqlite().setDatabase(":memory:");
MemoryThreePid mem3pid = new MemoryThreePid();
mem3pid.setMedium("email");
mem3pid.setAddress("john@localhost");
MemoryIdentityConfig validCfg = new MemoryIdentityConfig();
validCfg.setUsername("john");
validCfg.setPassword("doe");
validCfg.getThreepids().add(mem3pid);
MemoryIdentityConfig illegalUser = new MemoryIdentityConfig();
illegalUser.setUsername("JANE");
illegalUser.setPassword("doe");
cfg.getMemory().setEnabled(true);
cfg.getMemory().getIdentities().add(validCfg);
cfg.getMemory().getIdentities().add(illegalUser);
Mxisd m = new Mxisd(cfg);
m.start();
mgr = m.getAuth();
}
@Test
public void basic() {
UserAuthResult result = mgr.authenticate("@john:localhost", "doe");
assertTrue(result.isSuccess());
// For backward-compatibility as per instructed by the spec, we do not fail on an illegal username
// This makes sure we don't break it
result = mgr.authenticate("@JANE:localhost", "doe");
assertTrue(result.isSuccess());
}
}

View File

@@ -24,6 +24,7 @@ import com.google.gson.JsonObject;
import io.kamax.matrix.event.EventKey;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.matrix.json.MatrixJson;
import io.kamax.mxisd.config.MxisdConfig;
import io.kamax.mxisd.crypto.Signature;
import io.kamax.mxisd.crypto.SignatureManager;
import io.kamax.mxisd.crypto.ed25519.Ed25519Key;
@@ -52,7 +53,7 @@ public class SignatureManagerTest {
KeyStore store = new MemoryKeyStore();
store.add(key);
return new Ed25519SignatureManager(new Ed25519KeyManager(store));
return new Ed25519SignatureManager(MxisdConfig.forDomain("localhost").inMemory().build(), new Ed25519KeyManager(store));
}
@BeforeClass

View File

@@ -0,0 +1,36 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2019 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.test.util;
import io.kamax.mxisd.util.RestClientUtils;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class RestClientUtilsTest {
@Test
public void urlEncode() {
String encoded = RestClientUtils.urlEncode("john+doe@example.org");
assertEquals("john%2Bdoe%40example.org", encoded);
}
}