First prototype to validate phone numbers
This commit is contained in:
@@ -116,6 +116,9 @@ dependencies {
|
||||
// PostgreSQL
|
||||
compile 'org.postgresql:postgresql:42.1.4'
|
||||
|
||||
// Twilio SDK for SMS
|
||||
compile 'com.twilio.sdk:twilio:7.14.5'
|
||||
|
||||
testCompile 'junit:junit:4.12'
|
||||
testCompile 'com.github.tomakehurst:wiremock:2.8.0'
|
||||
}
|
||||
|
@@ -28,7 +28,7 @@ public enum UserIdType {
|
||||
Localpart("localpart"),
|
||||
MatrixID("mxid"),
|
||||
EmailLocalpart("email_localpart"),
|
||||
Email("email");
|
||||
Email("threepids/email");
|
||||
|
||||
private String id;
|
||||
|
||||
|
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.config.threepid.connector;
|
||||
|
||||
import io.kamax.mxisd.exception.ConfigurationException;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = PhoneTwilioConfig.NAMESPACE)
|
||||
public class PhoneTwilioConfig {
|
||||
|
||||
static final String NAMESPACE = "threepid.medium.msisdn.connectors.twilio";
|
||||
|
||||
private Logger log = LoggerFactory.getLogger(PhoneTwilioConfig.class);
|
||||
|
||||
private String accountSid;
|
||||
private String authToken;
|
||||
private String number;
|
||||
|
||||
public String getAccountSid() {
|
||||
return accountSid;
|
||||
}
|
||||
|
||||
public void setAccountSid(String accountSid) {
|
||||
this.accountSid = accountSid;
|
||||
}
|
||||
|
||||
public String getAuthToken() {
|
||||
return authToken;
|
||||
}
|
||||
|
||||
public void setAuthToken(String authToken) {
|
||||
this.authToken = authToken;
|
||||
}
|
||||
|
||||
public String getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public void setNumber(String number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void build() {
|
||||
log.info("--- Phone SMS Twilio connector config ---");
|
||||
|
||||
if (StringUtils.isBlank(getAccountSid())) {
|
||||
throw new ConfigurationException(NAMESPACE + ".accountSid");
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(getAuthToken())) {
|
||||
throw new ConfigurationException(NAMESPACE + ".authToken");
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(getNumber())) {
|
||||
throw new ConfigurationException(NAMESPACE + ".number");
|
||||
}
|
||||
|
||||
log.info("Account SID: {}", getAccountSid());
|
||||
log.info("Sender number: {}", getNumber());
|
||||
}
|
||||
|
||||
}
|
@@ -36,6 +36,8 @@ import javax.annotation.PostConstruct;
|
||||
@ConfigurationProperties("threepid.medium.email")
|
||||
public class EmailConfig {
|
||||
|
||||
private Logger log = LoggerFactory.getLogger(EmailConfig.class);
|
||||
|
||||
public static class Identity {
|
||||
private String from;
|
||||
private String name;
|
||||
@@ -61,8 +63,6 @@ public class EmailConfig {
|
||||
private String generator;
|
||||
private String connector;
|
||||
|
||||
private Logger log = LoggerFactory.getLogger(EmailConfig.class);
|
||||
|
||||
private MatrixConfig mxCfg;
|
||||
private Identity identity = new Identity();
|
||||
|
||||
|
@@ -20,7 +20,6 @@
|
||||
|
||||
package io.kamax.mxisd.config.threepid.medium;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
@@ -30,70 +29,9 @@ import javax.annotation.PostConstruct;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties("threepid.medium.email.generators.template")
|
||||
public class EmailTemplateConfig {
|
||||
public class EmailTemplateConfig extends GenericTemplateConfig {
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(EmailTemplateConfig.class);
|
||||
private static final String classpathPrefix = "classpath:";
|
||||
|
||||
private static String getName(String path) {
|
||||
if (StringUtils.startsWith(path, classpathPrefix)) {
|
||||
return "Built-in (" + path.substring(classpathPrefix.length()) + ")";
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static class Session {
|
||||
|
||||
public static class SessionValidation {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public void setValidation(SessionValidation validation) {
|
||||
this.validation = validation;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String invite;
|
||||
private Session session = new Session();
|
||||
|
||||
public String getInvite() {
|
||||
return invite;
|
||||
}
|
||||
|
||||
public void setInvite(String invite) {
|
||||
this.invite = invite;
|
||||
}
|
||||
|
||||
public Session getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void build() {
|
||||
|
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.config.threepid.medium;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
public class GenericTemplateConfig {
|
||||
|
||||
private static final String classpathPrefix = "classpath:";
|
||||
|
||||
protected static String getName(String path) {
|
||||
if (StringUtils.startsWith(path, classpathPrefix)) {
|
||||
return "Built-in (" + path.substring(classpathPrefix.length()) + ")";
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static class Session {
|
||||
|
||||
public static class SessionValidation {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public void setValidation(SessionValidation validation) {
|
||||
this.validation = validation;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String invite;
|
||||
private Session session = new Session();
|
||||
|
||||
public String getInvite() {
|
||||
return invite;
|
||||
}
|
||||
|
||||
public void setInvite(String invite) {
|
||||
this.invite = invite;
|
||||
}
|
||||
|
||||
public Session getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.config.threepid.medium;
|
||||
|
||||
import io.kamax.mxisd.exception.ConfigurationException;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties("threepid.medium.msisdn")
|
||||
public class PhoneConfig {
|
||||
|
||||
private Logger log = LoggerFactory.getLogger(PhoneConfig.class);
|
||||
|
||||
private String generator;
|
||||
private String connector;
|
||||
|
||||
public String getGenerator() {
|
||||
return generator;
|
||||
}
|
||||
|
||||
public void setGenerator(String generator) {
|
||||
this.generator = generator;
|
||||
}
|
||||
|
||||
public String getConnector() {
|
||||
return connector;
|
||||
}
|
||||
|
||||
public void setConnector(String connector) {
|
||||
this.connector = connector;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void build() {
|
||||
log.info("--- Phone config ---");
|
||||
|
||||
if (StringUtils.isBlank(getGenerator())) {
|
||||
throw new ConfigurationException("generator");
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(getConnector())) {
|
||||
throw new ConfigurationException("connector");
|
||||
}
|
||||
|
||||
log.info("Generator: {}", getGenerator());
|
||||
log.info("Connector: {}", getConnector());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.config.threepid.medium;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties("threepid.medium.msisdn.generators.template")
|
||||
public class PhoneSmsTemplateConfig extends GenericTemplateConfig {
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(EmailTemplateConfig.class);
|
||||
|
||||
@PostConstruct
|
||||
public void build() {
|
||||
log.info("--- SMS Generator templates config ---");
|
||||
log.info("Invite: {}", getName(getInvite()));
|
||||
log.info("Session validation:");
|
||||
log.info("\tLocal: {}", getName(getSession().getValidation().getLocal()));
|
||||
log.info("\tRemote: {}", getName(getSession().getValidation().getRemote()));
|
||||
}
|
||||
|
||||
}
|
@@ -51,6 +51,7 @@ public class DefaultExceptionHandler {
|
||||
JsonObject obj = new JsonObject();
|
||||
obj.addProperty("errcode", erroCode);
|
||||
obj.addProperty("error", error);
|
||||
obj.addProperty("success", false);
|
||||
return gson.toJson(obj);
|
||||
}
|
||||
|
||||
|
@@ -24,4 +24,9 @@ public class IdentityAPIv1 {
|
||||
|
||||
public static final String BASE = "/_matrix/identity/api/v1";
|
||||
|
||||
public static String getValidate(String medium, String sid, String secret, String token) {
|
||||
// FIXME use some kind of URLBuilder
|
||||
return BASE + "/validate/" + medium + "/submitToken?sid=" + sid + "&client_secret=" + secret + "&token=" + token;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -38,6 +38,8 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.GET;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(path = IdentityAPIv1.BASE)
|
||||
class SessionController {
|
||||
@@ -52,9 +54,8 @@ class SessionController {
|
||||
|
||||
@Autowired
|
||||
private ViewConfig viewCfg;
|
||||
;
|
||||
|
||||
@RequestMapping(value = "/validate/{medium}/submitToken")
|
||||
@RequestMapping(value = "/validate/{medium}/submitToken", method = GET)
|
||||
public String validate(
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
|
@@ -28,23 +28,28 @@ import io.kamax.mxisd.config.ServerConfig;
|
||||
import io.kamax.mxisd.config.ViewConfig;
|
||||
import io.kamax.mxisd.controller.v1.io.SessionEmailTokenRequestJson;
|
||||
import io.kamax.mxisd.controller.v1.io.SessionPhoneTokenRequestJson;
|
||||
import io.kamax.mxisd.controller.v1.io.SuccessStatusJson;
|
||||
import io.kamax.mxisd.exception.BadRequestException;
|
||||
import io.kamax.mxisd.exception.SessionNotValidatedException;
|
||||
import io.kamax.mxisd.invitation.InvitationManager;
|
||||
import io.kamax.mxisd.lookup.ThreePidValidation;
|
||||
import io.kamax.mxisd.session.SessionMananger;
|
||||
import io.kamax.mxisd.session.ValidationResult;
|
||||
import io.kamax.mxisd.util.GsonParser;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
||||
|
||||
@RestController
|
||||
@CrossOrigin
|
||||
@RequestMapping(path = IdentityAPIv1.BASE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
@@ -114,6 +119,23 @@ public class SessionRestController {
|
||||
return gson.toJson(obj);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/validate/{medium}/submitToken", method = POST)
|
||||
public String validate(
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
@RequestParam String sid,
|
||||
@RequestParam("client_secret") String secret,
|
||||
@RequestParam String token,
|
||||
Model model
|
||||
) {
|
||||
log.info("Requested: {}", request.getRequestURL());
|
||||
|
||||
ValidationResult r = mgr.validate(sid, secret, token);
|
||||
log.info("Session {} was validated", sid);
|
||||
|
||||
return gson.toJson(new SuccessStatusJson(true));
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/3pid/getValidated3pid")
|
||||
String check(HttpServletRequest request, HttpServletResponse response,
|
||||
@RequestParam String sid, @RequestParam("client_secret") String secret) {
|
||||
|
@@ -25,7 +25,7 @@ public class SessionEmailTokenRequestJson extends GenericTokenRequestJson {
|
||||
private String email;
|
||||
|
||||
public String getMedium() {
|
||||
return "email";
|
||||
return "threepids/email";
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
|
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.io;
|
||||
|
||||
public class SuccessStatusJson {
|
||||
|
||||
private boolean success;
|
||||
|
||||
public SuccessStatusJson(boolean success) {
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2017 Maxime Dor
|
||||
*
|
||||
* https://max.kamax.io/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.kamax.mxisd.exception;
|
||||
|
||||
import org.apache.http.HttpStatus;
|
||||
|
||||
public class MessageForClientException extends MatrixException {
|
||||
|
||||
public MessageForClientException(String error) {
|
||||
super(HttpStatus.SC_OK, "M_MESSAGE_FOR_CLIENT", error);
|
||||
}
|
||||
|
||||
}
|
@@ -25,7 +25,7 @@ import org.apache.http.HttpStatus;
|
||||
public class RemoteIdentityServerException extends MatrixException {
|
||||
|
||||
public RemoteIdentityServerException(String error) {
|
||||
super(HttpStatus.SC_SERVICE_UNAVAILABLE, "M_REMOTE_IS_ERROR", error);
|
||||
super(HttpStatus.SC_SERVICE_UNAVAILABLE, "M_REMOTE_IS_ERROR", "Error from remote server: " + error);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -83,7 +83,7 @@ class DnsLookupProvider implements IThreePidProvider {
|
||||
|
||||
@Override
|
||||
public Optional<SingleLookupReply> find(SingleLookupRequest request) {
|
||||
if (!StringUtils.equals("email", request.getType())) { // TODO use enum
|
||||
if (!StringUtils.equals("threepids/email", request.getType())) { // TODO use enum
|
||||
log.info("Skipping unsupported type {} for {}", request.getType(), request.getThreePid());
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -106,7 +106,7 @@ class DnsLookupProvider implements IThreePidProvider {
|
||||
Map<String, List<ThreePidMapping>> domains = new HashMap<>();
|
||||
|
||||
for (ThreePidMapping mapping : mappings) {
|
||||
if (!StringUtils.equals("email", mapping.getMedium())) {
|
||||
if (!StringUtils.equals("threepids/email", mapping.getMedium())) {
|
||||
log.info("Skipping unsupported type {} for {}", mapping.getMedium(), mapping.getValue());
|
||||
continue;
|
||||
}
|
||||
|
@@ -21,7 +21,7 @@ import java.util.Optional;
|
||||
// FIXME placeholder, this must go in matrix-java-sdk for 1.0
|
||||
public class IdentityServerUtils {
|
||||
|
||||
public static final String THREEPID_TEST_MEDIUM = "email";
|
||||
public static final String THREEPID_TEST_MEDIUM = "threepids/email";
|
||||
public static final String THREEPID_TEST_ADDRESS = "mxisd-email-forever-unknown@forever-invalid.kamax.io";
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(IdentityServerUtils.class);
|
||||
|
@@ -21,6 +21,9 @@
|
||||
package io.kamax.mxisd.session;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.i18n.phonenumbers.NumberParseException;
|
||||
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
||||
import com.google.i18n.phonenumbers.Phonenumber;
|
||||
import io.kamax.matrix.MatrixID;
|
||||
import io.kamax.matrix.ThreePidMedium;
|
||||
import io.kamax.matrix._MatrixID;
|
||||
@@ -73,6 +76,9 @@ public class SessionMananger {
|
||||
private IStorage storage;
|
||||
private NotificationManager notifMgr;
|
||||
|
||||
private GsonParser parser = new GsonParser();
|
||||
private PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance(); // FIXME refactor for sessions handling their own stuff
|
||||
|
||||
// FIXME export into central class, set version
|
||||
private CloseableHttpClient client = HttpClients.custom().setUserAgent("mxisd").build();
|
||||
|
||||
@@ -180,9 +186,21 @@ public class SessionMananger {
|
||||
throw new NotAllowedException("Validating " + (isLocal ? "local" : "remote") + " 3PID is not allowed");
|
||||
}
|
||||
|
||||
if (ThreePidMedium.PhoneNumber.is(session.getThreePid().getMedium()) && session.isValidated() && session.isRemote()) {
|
||||
submitRemote(session, token);
|
||||
session.validateRemote();
|
||||
return new ValidationResult(session, false);
|
||||
}
|
||||
|
||||
session.validate(token);
|
||||
storage.updateThreePidSession(session.getDao());
|
||||
log.info("Session {} has been validated", session.getId());
|
||||
log.info("Session {} has been validated locally", session.getId());
|
||||
|
||||
if (ThreePidMedium.PhoneNumber.is(session.getThreePid().getMedium()) && session.isValidated()) {
|
||||
createRemote(sid, secret);
|
||||
// FIXME make the message configurable/customizable (templates?)
|
||||
throw new MessageForClientException("You will receive a NEW code from another number. Enter it below");
|
||||
}
|
||||
|
||||
// FIXME definitely doable in a nicer way
|
||||
ValidationResult r = new ValidationResult(session, policy.toRemote());
|
||||
@@ -265,13 +283,22 @@ public class SessionMananger {
|
||||
body.addProperty("client_secret", remoteSecret);
|
||||
body.addProperty(session.getThreePid().getMedium(), session.getThreePid().getAddress());
|
||||
body.addProperty("send_attempt", session.increaseAndGetRemoteAttempt());
|
||||
try {
|
||||
Phonenumber.PhoneNumber msisdn = phoneUtil.parse("+" + session.getThreePid().getAddress(), null);
|
||||
String country = phoneUtil.getRegionCodeForNumber(msisdn).toUpperCase();
|
||||
body.addProperty("phone_number", phoneUtil.format(msisdn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL));
|
||||
body.addProperty("country", country);
|
||||
} catch (NumberParseException e) {
|
||||
throw new InternalServerError(e);
|
||||
}
|
||||
|
||||
log.info("Requesting remote session with attempt {}", session.getRemoteAttempt());
|
||||
HttpPost tokenReq = RestClientUtils.post(url + "/_matrix/identity/api/v1/validate/" + session.getThreePid().getMedium() + "/requestToken", body);
|
||||
try (CloseableHttpResponse response = client.execute(tokenReq)) {
|
||||
int status = response.getStatusLine().getStatusCode();
|
||||
if (status < 200 || status >= 300) {
|
||||
throw new RemoteIdentityServerException("Remote identity server returned with status " + status);
|
||||
JsonObject obj = parser.parseOptional(response).orElseThrow(() -> new RemoteIdentityServerException("Status " + status));
|
||||
throw new RemoteIdentityServerException(obj.get("errcode").getAsString() + ": " + obj.get("error").getAsString());
|
||||
}
|
||||
|
||||
RequestTokenResponse data = new GsonParser().parse(response, RequestTokenResponse.class);
|
||||
@@ -288,6 +315,29 @@ public class SessionMananger {
|
||||
}
|
||||
}
|
||||
|
||||
private void submitRemote(ThreePidSession session, String token) {
|
||||
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(
|
||||
Arrays.asList(
|
||||
new BasicNameValuePair("sid", session.getRemoteId()),
|
||||
new BasicNameValuePair("client_secret", session.getRemoteSecret()),
|
||||
new BasicNameValuePair("token", token)
|
||||
), StandardCharsets.UTF_8);
|
||||
HttpPost submitReq = new HttpPost(session.getRemoteServer() + "/_matrix/identity/api/v1/submitToken");
|
||||
submitReq.setEntity(entity);
|
||||
|
||||
try (CloseableHttpResponse response = client.execute(submitReq)) {
|
||||
JsonObject o = new GsonParser().parse(response.getEntity().getContent());
|
||||
if (!o.has("success") || !o.get("success").getAsBoolean()) {
|
||||
String errcode = o.get("errcode").getAsString();
|
||||
throw new RemoteIdentityServerException(errcode + ": " + o.get("error").getAsString());
|
||||
}
|
||||
|
||||
log.info("Successfully submitted validation token for {} to {}", session.getThreePid(), session.getRemoteServer());
|
||||
} catch (IOException e) {
|
||||
throw new RemoteIdentityServerException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void validateRemote(String sid, String secret) {
|
||||
ThreePidSession session = getSessionIfValidated(sid, secret);
|
||||
if (!session.isRemote()) {
|
||||
|
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.threepid.connector.phone;
|
||||
|
||||
import io.kamax.matrix.ThreePidMedium;
|
||||
import io.kamax.mxisd.threepid.connector.IThreePidConnector;
|
||||
|
||||
public interface IPhoneConnector extends IThreePidConnector {
|
||||
|
||||
@Override
|
||||
default String getMedium() {
|
||||
return ThreePidMedium.PhoneNumber.getId();
|
||||
}
|
||||
|
||||
void send(String recipient, String content);
|
||||
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.threepid.connector.phone;
|
||||
|
||||
import com.twilio.Twilio;
|
||||
import com.twilio.rest.api.v2010.account.Message;
|
||||
import com.twilio.type.PhoneNumber;
|
||||
import io.kamax.mxisd.config.threepid.connector.PhoneTwilioConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class PhoneSmsTwilioConnector implements IPhoneConnector {
|
||||
|
||||
private Logger log = LoggerFactory.getLogger(PhoneSmsTwilioConnector.class);
|
||||
|
||||
private PhoneTwilioConfig cfg;
|
||||
|
||||
@Autowired
|
||||
public PhoneSmsTwilioConnector(PhoneTwilioConfig cfg) {
|
||||
this.cfg = cfg;
|
||||
|
||||
Twilio.init(cfg.getAccountSid(), cfg.getAuthToken());
|
||||
log.info("Twilio API has been initiated");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "twilio";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(String recipient, String content) {
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.threepid.notification;
|
||||
|
||||
import io.kamax.mxisd.exception.ConfigurationException;
|
||||
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
||||
import io.kamax.mxisd.notification.INotificationHandler;
|
||||
import io.kamax.mxisd.threepid.connector.IThreePidConnector;
|
||||
import io.kamax.mxisd.threepid.session.IThreePidSession;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class GenericNotificationHandler<A extends IThreePidConnector, B extends INotificationGenerator> implements INotificationHandler {
|
||||
|
||||
private A connector;
|
||||
private B generator;
|
||||
|
||||
protected abstract String getConnectorId();
|
||||
|
||||
protected abstract String getGeneratorId();
|
||||
|
||||
protected abstract void send(A connector, String recipient, String content);
|
||||
|
||||
protected void process(List<A> connectors, List<B> generators) {
|
||||
generator = generators.stream()
|
||||
.filter(o -> StringUtils.equals(getGeneratorId(), o.getId()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new ConfigurationException(getMedium() + " notification generator [" +
|
||||
getGeneratorId() + "] could not be found"));
|
||||
|
||||
connector = connectors.stream()
|
||||
.filter(o -> StringUtils.equals(getConnectorId(), o.getId()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new ConfigurationException(getMedium() + " sender connector [" +
|
||||
getConnectorId() + "] could not be found"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendForInvite(IThreePidInviteReply invite) {
|
||||
send(connector, invite.getInvite().getAddress(), generator.getForInvite(invite));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendForValidation(IThreePidSession session) {
|
||||
send(connector, session.getThreePid().getAddress(), generator.getForValidation(session));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendForRemoteValidation(IThreePidSession session) {
|
||||
send(connector, session.getThreePid().getAddress(), generator.getForRemoteValidation(session));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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.threepid.notification;
|
||||
|
||||
import io.kamax.mxisd.ThreePid;
|
||||
import io.kamax.mxisd.config.MatrixConfig;
|
||||
import io.kamax.mxisd.config.ServerConfig;
|
||||
import io.kamax.mxisd.config.threepid.medium.GenericTemplateConfig;
|
||||
import io.kamax.mxisd.controller.v1.IdentityAPIv1;
|
||||
import io.kamax.mxisd.exception.InternalServerError;
|
||||
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
||||
import io.kamax.mxisd.threepid.session.IThreePidSession;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.WordUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@Component
|
||||
public abstract class GenericTemplateNotificationGenerator implements INotificationGenerator {
|
||||
|
||||
private Logger log = LoggerFactory.getLogger(GenericTemplateNotificationGenerator.class);
|
||||
|
||||
private MatrixConfig mxCfg;
|
||||
private ServerConfig srvCfg;
|
||||
private GenericTemplateConfig cfg;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext app;
|
||||
|
||||
public GenericTemplateNotificationGenerator(MatrixConfig mxCfg, ServerConfig srvCfg, GenericTemplateConfig cfg) {
|
||||
this.mxCfg = mxCfg;
|
||||
this.srvCfg = srvCfg;
|
||||
this.cfg = cfg;
|
||||
}
|
||||
|
||||
protected String populateForCommon(String body, ThreePid recipient) {
|
||||
return body;
|
||||
}
|
||||
|
||||
private String populateCommon(String body, ThreePid recipient) {
|
||||
body = populateForCommon(body, recipient);
|
||||
|
||||
String domainPretty = WordUtils.capitalizeFully(mxCfg.getDomain());
|
||||
body = body.replace("%DOMAIN%", mxCfg.getDomain());
|
||||
body = body.replace("%DOMAIN_PRETTY%", domainPretty);
|
||||
body = body.replace("%RECIPIENT_MEDIUM%", recipient.getMedium());
|
||||
body = body.replace("%RECIPIENT_ADDRESS%", recipient.getAddress());
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
private String getTemplateContent(String location) {
|
||||
try {
|
||||
InputStream is = StringUtils.startsWith(location, "classpath:") ?
|
||||
app.getResource(location).getInputStream() : new FileInputStream(location);
|
||||
return IOUtils.toString(is, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
throw new InternalServerError("Unable to read template content at " + location + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private String getTemplateAndPopulate(String location, ThreePid recipient) {
|
||||
return populateCommon(getTemplateContent(location), recipient);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getForInvite(IThreePidInviteReply invite) {
|
||||
ThreePid tpid = new ThreePid(invite.getInvite().getMedium(), invite.getInvite().getAddress());
|
||||
String templateBody = getTemplateAndPopulate(cfg.getInvite(), tpid);
|
||||
|
||||
String senderName = invite.getInvite().getProperties().getOrDefault("sender_display_name", "");
|
||||
String senderNameOrId = StringUtils.defaultIfBlank(senderName, invite.getInvite().getSender().getId());
|
||||
String roomName = invite.getInvite().getProperties().getOrDefault("room_name", "");
|
||||
String roomNameOrId = StringUtils.defaultIfBlank(roomName, invite.getInvite().getRoomId());
|
||||
|
||||
templateBody = templateBody.replace("%SENDER_ID%", invite.getInvite().getSender().getId());
|
||||
templateBody = templateBody.replace("%SENDER_NAME%", senderName);
|
||||
templateBody = templateBody.replace("%SENDER_NAME_OR_ID%", senderNameOrId);
|
||||
templateBody = templateBody.replace("%INVITE_MEDIUM%", tpid.getMedium());
|
||||
templateBody = templateBody.replace("%INVITE_ADDRESS%", tpid.getAddress());
|
||||
templateBody = templateBody.replace("%ROOM_ID%", invite.getInvite().getRoomId());
|
||||
templateBody = templateBody.replace("%ROOM_NAME%", roomName);
|
||||
templateBody = templateBody.replace("%ROOM_NAME_OR_ID%", roomNameOrId);
|
||||
|
||||
return templateBody;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getForValidation(IThreePidSession session) {
|
||||
log.info("Generating notification content for 3PID Session validation");
|
||||
String templateBody = getTemplateAndPopulate(cfg.getSession().getValidation().getLocal(), session.getThreePid());
|
||||
|
||||
String validationLink = srvCfg.getPublicUrl() + IdentityAPIv1.getValidate(
|
||||
session.getThreePid().getMedium(),
|
||||
session.getId(),
|
||||
session.getSecret(),
|
||||
session.getToken());
|
||||
|
||||
templateBody = templateBody.replace("%VALIDATION_LINK%", validationLink);
|
||||
templateBody = templateBody.replace("%VALIDATION_TOKEN%", session.getToken());
|
||||
|
||||
return templateBody;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getForRemoteValidation(IThreePidSession session) {
|
||||
log.info("Generating notification content for remote-only 3PID session");
|
||||
String templateBody = getTemplateAndPopulate(cfg.getSession().getValidation().getRemote(), session.getThreePid());
|
||||
|
||||
String validationLink = srvCfg.getPublicUrl() + IdentityAPIv1.getValidate(
|
||||
session.getThreePid().getMedium(),
|
||||
session.getId(),
|
||||
session.getSecret(),
|
||||
session.getToken());
|
||||
|
||||
templateBody = templateBody.replace("%VALIDATION_LINK%", validationLink);
|
||||
templateBody = templateBody.replace("%VALIDATION_TOKEN%", session.getToken());
|
||||
templateBody = templateBody.replace("%NEXT_URL%", validationLink);
|
||||
|
||||
return templateBody;
|
||||
}
|
||||
|
||||
}
|
@@ -25,43 +25,19 @@ import io.kamax.mxisd.config.MatrixConfig;
|
||||
import io.kamax.mxisd.config.ServerConfig;
|
||||
import io.kamax.mxisd.config.threepid.medium.EmailConfig;
|
||||
import io.kamax.mxisd.config.threepid.medium.EmailTemplateConfig;
|
||||
import io.kamax.mxisd.controller.v1.IdentityAPIv1;
|
||||
import io.kamax.mxisd.exception.InternalServerError;
|
||||
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
||||
import io.kamax.mxisd.threepid.session.IThreePidSession;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.WordUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import io.kamax.mxisd.threepid.notification.GenericTemplateNotificationGenerator;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@Component
|
||||
public class EmailNotificationGenerator implements IEmailNotificationGenerator {
|
||||
|
||||
private Logger log = LoggerFactory.getLogger(EmailNotificationGenerator.class);
|
||||
public class EmailNotificationGenerator extends GenericTemplateNotificationGenerator implements IEmailNotificationGenerator {
|
||||
|
||||
private EmailConfig cfg;
|
||||
private EmailTemplateConfig templateCfg;
|
||||
private MatrixConfig mxCfg;
|
||||
private ServerConfig srvCfg;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext app;
|
||||
|
||||
@Autowired
|
||||
public EmailNotificationGenerator(EmailTemplateConfig templateCfg, EmailConfig cfg, MatrixConfig mxCfg, ServerConfig srvCfg) {
|
||||
super(mxCfg, srvCfg, templateCfg);
|
||||
this.cfg = cfg;
|
||||
this.templateCfg = templateCfg;
|
||||
this.mxCfg = mxCfg;
|
||||
this.srvCfg = srvCfg;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -69,85 +45,11 @@ public class EmailNotificationGenerator implements IEmailNotificationGenerator {
|
||||
return "template";
|
||||
}
|
||||
|
||||
private String getTemplateContent(String location) {
|
||||
try {
|
||||
InputStream is = StringUtils.startsWith(location, "classpath:") ?
|
||||
app.getResource(location).getInputStream() : new FileInputStream(location);
|
||||
return IOUtils.toString(is, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
throw new InternalServerError("Unable to read template content at " + location + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private String populateCommon(String content, ThreePid recipient) {
|
||||
String domainPretty = WordUtils.capitalizeFully(mxCfg.getDomain());
|
||||
|
||||
content = content.replace("%DOMAIN%", mxCfg.getDomain());
|
||||
content = content.replace("%DOMAIN_PRETTY%", domainPretty);
|
||||
content = content.replace("%FROM_EMAIL%", cfg.getIdentity().getFrom());
|
||||
content = content.replace("%FROM_NAME%", cfg.getIdentity().getName());
|
||||
content = content.replace("%RECIPIENT_MEDIUM%", recipient.getMedium());
|
||||
content = content.replace("%RECIPIENT_ADDRESS%", recipient.getAddress());
|
||||
return content;
|
||||
}
|
||||
|
||||
private String getTemplateAndPopulate(String location, ThreePid recipient) {
|
||||
return populateCommon(getTemplateContent(location), recipient);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getForInvite(IThreePidInviteReply invite) {
|
||||
ThreePid tpid = new ThreePid(invite.getInvite().getMedium(), invite.getInvite().getAddress());
|
||||
String templateBody = getTemplateAndPopulate(templateCfg.getInvite(), tpid);
|
||||
|
||||
String senderName = invite.getInvite().getProperties().getOrDefault("sender_display_name", "");
|
||||
String senderNameOrId = StringUtils.defaultIfBlank(senderName, invite.getInvite().getSender().getId());
|
||||
String roomName = invite.getInvite().getProperties().getOrDefault("room_name", "");
|
||||
String roomNameOrId = StringUtils.defaultIfBlank(roomName, invite.getInvite().getRoomId());
|
||||
|
||||
templateBody = templateBody.replace("%SENDER_ID%", invite.getInvite().getSender().getId());
|
||||
templateBody = templateBody.replace("%SENDER_NAME%", senderName);
|
||||
templateBody = templateBody.replace("%SENDER_NAME_OR_ID%", senderNameOrId);
|
||||
templateBody = templateBody.replace("%INVITE_MEDIUM%", tpid.getMedium());
|
||||
templateBody = templateBody.replace("%INVITE_ADDRESS%", tpid.getAddress());
|
||||
templateBody = templateBody.replace("%ROOM_ID%", invite.getInvite().getRoomId());
|
||||
templateBody = templateBody.replace("%ROOM_NAME%", roomName);
|
||||
templateBody = templateBody.replace("%ROOM_NAME_OR_ID%", roomNameOrId);
|
||||
|
||||
return templateBody;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getForValidation(IThreePidSession session) {
|
||||
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, most likely in the SDK?
|
||||
String validationLink = srvCfg.getPublicUrl() + IdentityAPIv1.BASE +
|
||||
"/validate/" + session.getThreePid().getMedium() +
|
||||
"/submitToken?sid=" + session.getId() + "&client_secret=" + session.getSecret() +
|
||||
"&token=" + session.getToken();
|
||||
|
||||
templateBody = templateBody.replace("%VALIDATION_LINK%", validationLink);
|
||||
templateBody = templateBody.replace("%VALIDATION_TOKEN%", session.getToken());
|
||||
|
||||
return templateBody;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getForRemoteValidation(IThreePidSession session) {
|
||||
log.info("Generating notification content for remote-only 3PID session");
|
||||
String templateBody = getTemplateAndPopulate(templateCfg.getSession().getValidation().getRemote(), session.getThreePid());
|
||||
|
||||
// FIXME should have a global link builder, most likely in the SDK?
|
||||
String validationLink = srvCfg.getPublicUrl() + IdentityAPIv1.BASE +
|
||||
"/validate/" + session.getThreePid().getMedium() +
|
||||
"/submitToken?sid=" + session.getId() + "&client_secret=" + session.getSecret() +
|
||||
"&token=" + session.getToken();
|
||||
|
||||
templateBody = templateBody.replace("%NEXT_URL%", validationLink);
|
||||
|
||||
return templateBody;
|
||||
protected String populateForCommon(String body, ThreePid recipient) {
|
||||
body = body.replace("%FROM_EMAIL%", cfg.getIdentity().getFrom());
|
||||
body = body.replace("%FROM_NAME%", cfg.getIdentity().getName());
|
||||
return body;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -22,37 +22,22 @@ package io.kamax.mxisd.threepid.notification.email;
|
||||
|
||||
import io.kamax.matrix.ThreePidMedium;
|
||||
import io.kamax.mxisd.config.threepid.medium.EmailConfig;
|
||||
import io.kamax.mxisd.exception.ConfigurationException;
|
||||
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
||||
import io.kamax.mxisd.notification.INotificationHandler;
|
||||
import io.kamax.mxisd.threepid.connector.email.IEmailConnector;
|
||||
import io.kamax.mxisd.threepid.session.IThreePidSession;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import io.kamax.mxisd.threepid.notification.GenericNotificationHandler;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class EmailNotificationHandler implements INotificationHandler {
|
||||
public class EmailNotificationHandler extends GenericNotificationHandler<IEmailConnector, IEmailNotificationGenerator> {
|
||||
|
||||
private EmailConfig cfg;
|
||||
private IEmailNotificationGenerator generator;
|
||||
private IEmailConnector connector;
|
||||
|
||||
@Autowired
|
||||
public EmailNotificationHandler(EmailConfig cfg, List<IEmailNotificationGenerator> generators, List<IEmailConnector> connectors) {
|
||||
this.cfg = cfg;
|
||||
|
||||
generator = generators.stream()
|
||||
.filter(o -> StringUtils.equals(cfg.getGenerator(), o.getId()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new ConfigurationException("Email notification generator [" + cfg.getGenerator() + "] could not be found"));
|
||||
|
||||
connector = connectors.stream()
|
||||
.filter(o -> StringUtils.equals(cfg.getConnector(), o.getId()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new ConfigurationException("Email sender connector [" + cfg.getConnector() + "] could not be found"));
|
||||
process(connectors, generators);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,7 +45,18 @@ public class EmailNotificationHandler implements INotificationHandler {
|
||||
return ThreePidMedium.Email.getId();
|
||||
}
|
||||
|
||||
private void send(String recipient, String content) {
|
||||
@Override
|
||||
protected String getConnectorId() {
|
||||
return cfg.getConnector();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getGeneratorId() {
|
||||
return cfg.getGenerator();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void send(IEmailConnector connector, String recipient, String content) {
|
||||
connector.send(
|
||||
cfg.getIdentity().getFrom(),
|
||||
cfg.getIdentity().getName(),
|
||||
@@ -69,19 +65,4 @@ public class EmailNotificationHandler implements INotificationHandler {
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendForInvite(IThreePidInviteReply invite) {
|
||||
send(invite.getInvite().getAddress(), generator.getForInvite(invite));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendForValidation(IThreePidSession session) {
|
||||
send(session.getThreePid().getAddress(), generator.getForValidation(session));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendForRemoteValidation(IThreePidSession session) {
|
||||
send(session.getThreePid().getAddress(), generator.getForRemoteValidation(session));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2017 Maxime Dor
|
||||
*
|
||||
* https://max.kamax.io/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.kamax.mxisd.threepid.notification.phone;
|
||||
|
||||
import io.kamax.mxisd.threepid.notification.INotificationGenerator;
|
||||
|
||||
public interface IPhoneNotificationGenerator extends INotificationGenerator {
|
||||
|
||||
default String getMedium() {
|
||||
return "msisdn";
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.threepid.notification.phone;
|
||||
|
||||
import io.kamax.matrix.ThreePidMedium;
|
||||
import io.kamax.mxisd.config.threepid.medium.PhoneConfig;
|
||||
import io.kamax.mxisd.threepid.connector.phone.IPhoneConnector;
|
||||
import io.kamax.mxisd.threepid.notification.GenericNotificationHandler;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class PhoneNotificationHandler extends GenericNotificationHandler<IPhoneConnector, IPhoneNotificationGenerator> {
|
||||
|
||||
private PhoneConfig cfg;
|
||||
|
||||
@Autowired
|
||||
public PhoneNotificationHandler(PhoneConfig cfg, List<IPhoneConnector> connectors, List<IPhoneNotificationGenerator> generators) {
|
||||
this.cfg = cfg;
|
||||
process(connectors, generators);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMedium() {
|
||||
return ThreePidMedium.PhoneNumber.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getConnectorId() {
|
||||
return cfg.getConnector();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getGeneratorId() {
|
||||
return cfg.getGenerator();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void send(IPhoneConnector connector, String recipient, String content) {
|
||||
connector.send(recipient, content);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.threepid.notification.phone;
|
||||
|
||||
import io.kamax.mxisd.config.MatrixConfig;
|
||||
import io.kamax.mxisd.config.ServerConfig;
|
||||
import io.kamax.mxisd.config.threepid.medium.PhoneSmsTemplateConfig;
|
||||
import io.kamax.mxisd.threepid.notification.GenericTemplateNotificationGenerator;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class SmsNotificationGenerator extends GenericTemplateNotificationGenerator implements IPhoneNotificationGenerator {
|
||||
|
||||
public SmsNotificationGenerator(MatrixConfig mxCfg, ServerConfig srvCfg, PhoneSmsTemplateConfig cfg) {
|
||||
super(mxCfg, srvCfg, cfg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "template";
|
||||
}
|
||||
|
||||
}
|
@@ -62,6 +62,14 @@ public class GsonParser {
|
||||
return gson.fromJson(parse(res.getEntity().getContent()), type);
|
||||
}
|
||||
|
||||
public Optional<JsonObject> parseOptional(HttpResponse res) {
|
||||
try {
|
||||
return Optional.of(parse(res.getEntity().getContent()));
|
||||
} catch (IOException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public JsonObject parse(InputStream stream, String property) throws IOException {
|
||||
JsonObject obj = parse(stream);
|
||||
if (!obj.has(property)) {
|
||||
|
@@ -92,11 +92,26 @@ threepid:
|
||||
password: ''
|
||||
generators:
|
||||
template:
|
||||
invite: 'classpath:email/invite-template.eml'
|
||||
invite: 'classpath:threepids/email/invite-template.eml'
|
||||
session:
|
||||
validation:
|
||||
local: 'classpath:email/validate-local-template.eml'
|
||||
remote: 'classpath:email/validate-remote-template.eml'
|
||||
local: 'classpath:threepids/email/validate-local-template.eml'
|
||||
remote: 'classpath:threepids/email/validate-remote-template.eml'
|
||||
msisdn:
|
||||
connector: 'twilio'
|
||||
generator: 'template'
|
||||
connectors:
|
||||
twilio:
|
||||
accountSid: ''
|
||||
authToken: ''
|
||||
number: ''
|
||||
generators:
|
||||
template:
|
||||
invite: 'classpath:threepids/sms/invite-template.txt'
|
||||
session:
|
||||
validation:
|
||||
local: 'classpath:threepids/sms/validate-local-template.txt'
|
||||
remote: 'classpath:threepids/sms/validate-remote-template.txt'
|
||||
|
||||
session:
|
||||
policy:
|
||||
|
1
src/main/resources/threepids/sms/invite-template.txt
Normal file
1
src/main/resources/threepids/sms/invite-template.txt
Normal file
@@ -0,0 +1 @@
|
||||
You have been invited to a Matrix room by %SENDER_NAME_OR_ID%. Visit https://riot.im/ or any public server to join and start chatting!
|
@@ -0,0 +1 @@
|
||||
Your Matrix token is %VALIDATION_TOKEN%
|
@@ -0,0 +1 @@
|
||||
Your phone number will be made publicly searchable. To continue, you will need to enter two codes. Your first code is %VALIDATION_TOKEN%
|
Reference in New Issue
Block a user