First prototype to validate phone numbers
This commit is contained in:
@@ -116,6 +116,9 @@ dependencies {
|
|||||||
// PostgreSQL
|
// PostgreSQL
|
||||||
compile 'org.postgresql:postgresql:42.1.4'
|
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 'junit:junit:4.12'
|
||||||
testCompile 'com.github.tomakehurst:wiremock:2.8.0'
|
testCompile 'com.github.tomakehurst:wiremock:2.8.0'
|
||||||
}
|
}
|
||||||
|
@@ -28,7 +28,7 @@ public enum UserIdType {
|
|||||||
Localpart("localpart"),
|
Localpart("localpart"),
|
||||||
MatrixID("mxid"),
|
MatrixID("mxid"),
|
||||||
EmailLocalpart("email_localpart"),
|
EmailLocalpart("email_localpart"),
|
||||||
Email("email");
|
Email("threepids/email");
|
||||||
|
|
||||||
private String id;
|
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")
|
@ConfigurationProperties("threepid.medium.email")
|
||||||
public class EmailConfig {
|
public class EmailConfig {
|
||||||
|
|
||||||
|
private Logger log = LoggerFactory.getLogger(EmailConfig.class);
|
||||||
|
|
||||||
public static class Identity {
|
public static class Identity {
|
||||||
private String from;
|
private String from;
|
||||||
private String name;
|
private String name;
|
||||||
@@ -61,8 +63,6 @@ public class EmailConfig {
|
|||||||
private String generator;
|
private String generator;
|
||||||
private String connector;
|
private String connector;
|
||||||
|
|
||||||
private Logger log = LoggerFactory.getLogger(EmailConfig.class);
|
|
||||||
|
|
||||||
private MatrixConfig mxCfg;
|
private MatrixConfig mxCfg;
|
||||||
private Identity identity = new Identity();
|
private Identity identity = new Identity();
|
||||||
|
|
||||||
|
@@ -20,7 +20,6 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.config.threepid.medium;
|
package io.kamax.mxisd.config.threepid.medium;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
@@ -30,70 +29,9 @@ import javax.annotation.PostConstruct;
|
|||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConfigurationProperties("threepid.medium.email.generators.template")
|
@ConfigurationProperties("threepid.medium.email.generators.template")
|
||||||
public class EmailTemplateConfig {
|
public class EmailTemplateConfig extends GenericTemplateConfig {
|
||||||
|
|
||||||
private static Logger log = LoggerFactory.getLogger(EmailTemplateConfig.class);
|
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
|
@PostConstruct
|
||||||
public void build() {
|
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();
|
JsonObject obj = new JsonObject();
|
||||||
obj.addProperty("errcode", erroCode);
|
obj.addProperty("errcode", erroCode);
|
||||||
obj.addProperty("error", error);
|
obj.addProperty("error", error);
|
||||||
|
obj.addProperty("success", false);
|
||||||
return gson.toJson(obj);
|
return gson.toJson(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,4 +24,9 @@ public class IdentityAPIv1 {
|
|||||||
|
|
||||||
public static final String BASE = "/_matrix/identity/api/v1";
|
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 javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.springframework.web.bind.annotation.RequestMethod.GET;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
@RequestMapping(path = IdentityAPIv1.BASE)
|
@RequestMapping(path = IdentityAPIv1.BASE)
|
||||||
class SessionController {
|
class SessionController {
|
||||||
@@ -52,9 +54,8 @@ class SessionController {
|
|||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ViewConfig viewCfg;
|
private ViewConfig viewCfg;
|
||||||
;
|
|
||||||
|
|
||||||
@RequestMapping(value = "/validate/{medium}/submitToken")
|
@RequestMapping(value = "/validate/{medium}/submitToken", method = GET)
|
||||||
public String validate(
|
public String validate(
|
||||||
HttpServletRequest request,
|
HttpServletRequest request,
|
||||||
HttpServletResponse response,
|
HttpServletResponse response,
|
||||||
|
@@ -28,23 +28,28 @@ import io.kamax.mxisd.config.ServerConfig;
|
|||||||
import io.kamax.mxisd.config.ViewConfig;
|
import io.kamax.mxisd.config.ViewConfig;
|
||||||
import io.kamax.mxisd.controller.v1.io.SessionEmailTokenRequestJson;
|
import io.kamax.mxisd.controller.v1.io.SessionEmailTokenRequestJson;
|
||||||
import io.kamax.mxisd.controller.v1.io.SessionPhoneTokenRequestJson;
|
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.BadRequestException;
|
||||||
import io.kamax.mxisd.exception.SessionNotValidatedException;
|
import io.kamax.mxisd.exception.SessionNotValidatedException;
|
||||||
import io.kamax.mxisd.invitation.InvitationManager;
|
import io.kamax.mxisd.invitation.InvitationManager;
|
||||||
import io.kamax.mxisd.lookup.ThreePidValidation;
|
import io.kamax.mxisd.lookup.ThreePidValidation;
|
||||||
import io.kamax.mxisd.session.SessionMananger;
|
import io.kamax.mxisd.session.SessionMananger;
|
||||||
|
import io.kamax.mxisd.session.ValidationResult;
|
||||||
import io.kamax.mxisd.util.GsonParser;
|
import io.kamax.mxisd.util.GsonParser;
|
||||||
import org.apache.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@CrossOrigin
|
@CrossOrigin
|
||||||
@RequestMapping(path = IdentityAPIv1.BASE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
@RequestMapping(path = IdentityAPIv1.BASE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||||
@@ -114,6 +119,23 @@ public class SessionRestController {
|
|||||||
return gson.toJson(obj);
|
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")
|
@RequestMapping(value = "/3pid/getValidated3pid")
|
||||||
String check(HttpServletRequest request, HttpServletResponse response,
|
String check(HttpServletRequest request, HttpServletResponse response,
|
||||||
@RequestParam String sid, @RequestParam("client_secret") String secret) {
|
@RequestParam String sid, @RequestParam("client_secret") String secret) {
|
||||||
|
@@ -25,7 +25,7 @@ public class SessionEmailTokenRequestJson extends GenericTokenRequestJson {
|
|||||||
private String email;
|
private String email;
|
||||||
|
|
||||||
public String getMedium() {
|
public String getMedium() {
|
||||||
return "email";
|
return "threepids/email";
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getValue() {
|
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 class RemoteIdentityServerException extends MatrixException {
|
||||||
|
|
||||||
public RemoteIdentityServerException(String error) {
|
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
|
@Override
|
||||||
public Optional<SingleLookupReply> find(SingleLookupRequest request) {
|
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());
|
log.info("Skipping unsupported type {} for {}", request.getType(), request.getThreePid());
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
@@ -106,7 +106,7 @@ class DnsLookupProvider implements IThreePidProvider {
|
|||||||
Map<String, List<ThreePidMapping>> domains = new HashMap<>();
|
Map<String, List<ThreePidMapping>> domains = new HashMap<>();
|
||||||
|
|
||||||
for (ThreePidMapping mapping : mappings) {
|
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());
|
log.info("Skipping unsupported type {} for {}", mapping.getMedium(), mapping.getValue());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,7 @@ import java.util.Optional;
|
|||||||
// FIXME placeholder, this must go in matrix-java-sdk for 1.0
|
// FIXME placeholder, this must go in matrix-java-sdk for 1.0
|
||||||
public class IdentityServerUtils {
|
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";
|
public static final String THREEPID_TEST_ADDRESS = "mxisd-email-forever-unknown@forever-invalid.kamax.io";
|
||||||
|
|
||||||
private static Logger log = LoggerFactory.getLogger(IdentityServerUtils.class);
|
private static Logger log = LoggerFactory.getLogger(IdentityServerUtils.class);
|
||||||
|
@@ -21,6 +21,9 @@
|
|||||||
package io.kamax.mxisd.session;
|
package io.kamax.mxisd.session;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
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.MatrixID;
|
||||||
import io.kamax.matrix.ThreePidMedium;
|
import io.kamax.matrix.ThreePidMedium;
|
||||||
import io.kamax.matrix._MatrixID;
|
import io.kamax.matrix._MatrixID;
|
||||||
@@ -73,6 +76,9 @@ public class SessionMananger {
|
|||||||
private IStorage storage;
|
private IStorage storage;
|
||||||
private NotificationManager notifMgr;
|
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
|
// FIXME export into central class, set version
|
||||||
private CloseableHttpClient client = HttpClients.custom().setUserAgent("mxisd").build();
|
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");
|
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);
|
session.validate(token);
|
||||||
storage.updateThreePidSession(session.getDao());
|
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
|
// FIXME definitely doable in a nicer way
|
||||||
ValidationResult r = new ValidationResult(session, policy.toRemote());
|
ValidationResult r = new ValidationResult(session, policy.toRemote());
|
||||||
@@ -265,13 +283,22 @@ public class SessionMananger {
|
|||||||
body.addProperty("client_secret", remoteSecret);
|
body.addProperty("client_secret", remoteSecret);
|
||||||
body.addProperty(session.getThreePid().getMedium(), session.getThreePid().getAddress());
|
body.addProperty(session.getThreePid().getMedium(), session.getThreePid().getAddress());
|
||||||
body.addProperty("send_attempt", session.increaseAndGetRemoteAttempt());
|
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());
|
log.info("Requesting remote session with attempt {}", session.getRemoteAttempt());
|
||||||
HttpPost tokenReq = RestClientUtils.post(url + "/_matrix/identity/api/v1/validate/" + session.getThreePid().getMedium() + "/requestToken", body);
|
HttpPost tokenReq = RestClientUtils.post(url + "/_matrix/identity/api/v1/validate/" + session.getThreePid().getMedium() + "/requestToken", body);
|
||||||
try (CloseableHttpResponse response = client.execute(tokenReq)) {
|
try (CloseableHttpResponse response = client.execute(tokenReq)) {
|
||||||
int status = response.getStatusLine().getStatusCode();
|
int status = response.getStatusLine().getStatusCode();
|
||||||
if (status < 200 || status >= 300) {
|
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);
|
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) {
|
public void validateRemote(String sid, String secret) {
|
||||||
ThreePidSession session = getSessionIfValidated(sid, secret);
|
ThreePidSession session = getSessionIfValidated(sid, secret);
|
||||||
if (!session.isRemote()) {
|
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.ServerConfig;
|
||||||
import io.kamax.mxisd.config.threepid.medium.EmailConfig;
|
import io.kamax.mxisd.config.threepid.medium.EmailConfig;
|
||||||
import io.kamax.mxisd.config.threepid.medium.EmailTemplateConfig;
|
import io.kamax.mxisd.config.threepid.medium.EmailTemplateConfig;
|
||||||
import io.kamax.mxisd.controller.v1.IdentityAPIv1;
|
import io.kamax.mxisd.threepid.notification.GenericTemplateNotificationGenerator;
|
||||||
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.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.ApplicationContext;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class EmailNotificationGenerator implements IEmailNotificationGenerator {
|
public class EmailNotificationGenerator extends GenericTemplateNotificationGenerator implements IEmailNotificationGenerator {
|
||||||
|
|
||||||
private Logger log = LoggerFactory.getLogger(EmailNotificationGenerator.class);
|
|
||||||
|
|
||||||
private EmailConfig cfg;
|
private EmailConfig cfg;
|
||||||
private EmailTemplateConfig templateCfg;
|
|
||||||
private MatrixConfig mxCfg;
|
|
||||||
private ServerConfig srvCfg;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ApplicationContext app;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public EmailNotificationGenerator(EmailTemplateConfig templateCfg, EmailConfig cfg, MatrixConfig mxCfg, ServerConfig srvCfg) {
|
public EmailNotificationGenerator(EmailTemplateConfig templateCfg, EmailConfig cfg, MatrixConfig mxCfg, ServerConfig srvCfg) {
|
||||||
|
super(mxCfg, srvCfg, templateCfg);
|
||||||
this.cfg = cfg;
|
this.cfg = cfg;
|
||||||
this.templateCfg = templateCfg;
|
|
||||||
this.mxCfg = mxCfg;
|
|
||||||
this.srvCfg = srvCfg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -69,85 +45,11 @@ public class EmailNotificationGenerator implements IEmailNotificationGenerator {
|
|||||||
return "template";
|
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
|
@Override
|
||||||
public String getForInvite(IThreePidInviteReply invite) {
|
protected String populateForCommon(String body, ThreePid recipient) {
|
||||||
ThreePid tpid = new ThreePid(invite.getInvite().getMedium(), invite.getInvite().getAddress());
|
body = body.replace("%FROM_EMAIL%", cfg.getIdentity().getFrom());
|
||||||
String templateBody = getTemplateAndPopulate(templateCfg.getInvite(), tpid);
|
body = body.replace("%FROM_NAME%", cfg.getIdentity().getName());
|
||||||
|
return body;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -22,37 +22,22 @@ package io.kamax.mxisd.threepid.notification.email;
|
|||||||
|
|
||||||
import io.kamax.matrix.ThreePidMedium;
|
import io.kamax.matrix.ThreePidMedium;
|
||||||
import io.kamax.mxisd.config.threepid.medium.EmailConfig;
|
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.connector.email.IEmailConnector;
|
||||||
import io.kamax.mxisd.threepid.session.IThreePidSession;
|
import io.kamax.mxisd.threepid.notification.GenericNotificationHandler;
|
||||||
import org.apache.commons.lang.StringUtils;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class EmailNotificationHandler implements INotificationHandler {
|
public class EmailNotificationHandler extends GenericNotificationHandler<IEmailConnector, IEmailNotificationGenerator> {
|
||||||
|
|
||||||
private EmailConfig cfg;
|
private EmailConfig cfg;
|
||||||
private IEmailNotificationGenerator generator;
|
|
||||||
private IEmailConnector connector;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public EmailNotificationHandler(EmailConfig cfg, List<IEmailNotificationGenerator> generators, List<IEmailConnector> connectors) {
|
public EmailNotificationHandler(EmailConfig cfg, List<IEmailNotificationGenerator> generators, List<IEmailConnector> connectors) {
|
||||||
this.cfg = cfg;
|
this.cfg = cfg;
|
||||||
|
process(connectors, generators);
|
||||||
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"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -60,7 +45,18 @@ public class EmailNotificationHandler implements INotificationHandler {
|
|||||||
return ThreePidMedium.Email.getId();
|
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(
|
connector.send(
|
||||||
cfg.getIdentity().getFrom(),
|
cfg.getIdentity().getFrom(),
|
||||||
cfg.getIdentity().getName(),
|
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);
|
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 {
|
public JsonObject parse(InputStream stream, String property) throws IOException {
|
||||||
JsonObject obj = parse(stream);
|
JsonObject obj = parse(stream);
|
||||||
if (!obj.has(property)) {
|
if (!obj.has(property)) {
|
||||||
|
@@ -92,11 +92,26 @@ threepid:
|
|||||||
password: ''
|
password: ''
|
||||||
generators:
|
generators:
|
||||||
template:
|
template:
|
||||||
invite: 'classpath:email/invite-template.eml'
|
invite: 'classpath:threepids/email/invite-template.eml'
|
||||||
session:
|
session:
|
||||||
validation:
|
validation:
|
||||||
local: 'classpath:email/validate-local-template.eml'
|
local: 'classpath:threepids/email/validate-local-template.eml'
|
||||||
remote: 'classpath:email/validate-remote-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:
|
session:
|
||||||
policy:
|
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