Merge pull request #32 from kamax-io/phone_numbers-validation

Phone numbers validation
This commit is contained in:
Max Dor
2017-09-25 17:12:59 +02:00
committed by GitHub
40 changed files with 1002 additions and 246 deletions

View File

@@ -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'
}

View File

@@ -6,6 +6,7 @@
- [Session scope](#session-scope)
- [Notifications](#notifications)
- [Email](#email)
- [Phone numbers](#msisdn-phone-numbers)
- [Usage](#usage)
- [Configuration](#configuration)
- [Web views](#web-views)
@@ -98,11 +99,17 @@ Built-in generators and connectors for supported 3PID types:
### Email
Generators:
- [Template](https://github.com/kamax-io/mxisd/blob/master/docs/threepids/email/notifications/template-generator.md)
- [Template](../threepids/notifications/template-generator.md)
Connectors:
- [SMTP](https://github.com/kamax-io/mxisd/blob/master/docs/threepids/email/notifications/smtp-connector.md)
- [SMTP](../threepids/medium/email/smtp-connector.md)
#### MSISDN (Phone numbers)
Generators:
- [Template](../threepids/notifications/template-generator.md)
Connectors:
- [Twilio](../threepids/medium/msisdn/twilio-connector.md) with SMS
## Usage
### Configuration
@@ -178,16 +185,6 @@ ID for each generator.
In the above example, emails notifications are generated by the `example2` module and sent with the `example1` module.
By default, `template` is used as generator and `smtp` as connector.
mxisd comes with the following IDs built-in:
**Connectors**
- `smtp` for a basic SMTP connector, attempting STARTLS by default.
See [the dedicated document](https://github.com/kamax-io/mxisd/tree/master/docs/threepids/email/notifications/smtp-connector.md)
**Generators**
- `template`, loading content from template files, using built-in mxisd templates by default.
See [the dedicated document](https://github.com/kamax-io/mxisd/tree/master/docs/threepids/email/notifications/template-generator.md)
for further configuration and customization options.
---
`session.policy.validation` is the core configuration to control what users configured to use your Identity server
@@ -210,7 +207,7 @@ Once a user click on a validation link, it is taken to the Identity Server valid
If the session or token is invalid, an error page is displayed.
Workflow pages are also available for the remote 3PID session process.
See [the dedicated document](https://github.com/kamax-io/mxisd/tree/master/docs/sessions/3pid-views.md)
See [the dedicated document](3pid-views.md)
on how to configure/customize/brand those pages to your liking.
### Scenarios

View File

@@ -1,5 +1,7 @@
# Email notifications - SMTP connector
The following configuration items are available:
Connector ID: `smtp`
Example configuration:
```
threepid:
medium:

View File

@@ -0,0 +1,15 @@
# SMS notifications - Twilio connector
Connector ID: `twilio`
Example configuration:
```
threepid:
medium:
msisdn:
connectors:
twilio:
accountSid: 'myAccountSid'
authToken: 'myAuthToken'
number: '+123456789'
```

View File

@@ -1,6 +1,6 @@
# Email notifications: Generate from templates
To create notification content, you can use the `template` generator which will read MIME email body, including headers,
encoded as UTF-8.
# Notifications: Generate from templates
To create notification content, you can use the `template` generator if supported for the 3PID medium which will read
content from configured files.
Placeholders can be integrated into the templates to dynamically populate such content with relevant information like
the 3PID that was requested, the domain of your Identity server, etc.
@@ -13,7 +13,7 @@ To configure paths to the various templates:
```
threepid:
medium:
email:
<YOUR 3PID MEDIUM HERE>:
generators:
template:
invite: '/path/to/invite-template.eml'
@@ -22,16 +22,16 @@ threepid:
local: '/path/to/validate-local-template.eml'
remote: 'path/to/validate-remote-template.eml'
```
The `template` generator is the default, so no further configuration is needed.
The `template` generator is usually the default, so no further configuration is needed.
## Global placeholders
| Placeholder | Purpose |
|-----------------------|------------------------------------------------------------------------------|
| `%DOMAIN%` | Identity server authoritative domain, as configured in `matrix.domain` |
| `%DOMAIN_PRETTY%` | Same as `%DOMAIN%` with the first letter upper case and all other lower case |
| `%FROM_EMAIL%` | Email address configured in `threepid.medium.email.identity.from` |
| `%FROM_NAME%` | Name configured in `threepid.medium.email.identity.name` |
| `%RECIPIENT_MEDIUM%` | Set as `email` |
| `%FROM_EMAIL%` | Email address configured in `threepid.medium.<3PID medium>.identity.from` |
| `%FROM_NAME%` | Name configured in `threepid.medium.<3PID medium>.identity.name` |
| `%RECIPIENT_MEDIUM%` | The 3PID medium, like `email` or `msisdn` |
| `%RECIPIENT_ADDRESS%` | The address to which the notification is sent |
## Events
@@ -43,14 +43,14 @@ This template is used when someone is invited into a room using an email address
| `%SENDER_ID%` | Matrix ID of the user who made the invite |
| `%SENDER_NAME%` | Display name of the user who made the invite, if not available/set, empty |
| `%SENDER_NAME_OR_ID%` | Display name of the user who made the invite. If not available/set, its Matrix ID |
| `%INVITE_MEDIUM%` | The 3PID medium for the invite. Always set to `email` |
| `%INVITE_MEDIUM%` | The 3PID medium for the invite. |
| `%INVITE_ADDRESS%` | The 3PID address for the invite. |
| `%ROOM_ID%` | The Matrix ID of the Room in which the invite took place |
| `%ROOM_NAME%` | The Name of the room in which the invite took place. If not available/set, empty |
| `%ROOM_NAME_OR_ID%` | The Name of the room in which the invite took place. If not available/set, its Matrix ID |
### Local validation of 3PID Session
This template is used when to user which added their email address to their profile/settings and the session policy
This template is used when to user which added their 3PID address to their profile/settings and the session policy
allows at least local sessions.
#### Placeholders
@@ -60,13 +60,14 @@ allows at least local sessions.
| `%VALIDATION_TOKEN%` | The token needed to validate the local session, in case the user cannot use the link |
### Remote validation of 3PID Session
This template is used when to user which added their email address to their profile/settings and the session policy only
This template is used when to user which added their 3PID address to their profile/settings and the session policy only
allows remote sessions.
**NOTE:** 3PID session always require local validation of a token, even if a remote session is enforced.
One cannot bind a MXID to the session until the remote
One cannot bind a MXID to the session until both local and remote sessions have been validated.
#### Placeholders
| Placeholder | Purpose |
|--------------|--------------------------------------------------------|
|----------------------|--------------------------------------------------------|
| `%VALIDATION_TOKEN%` | The token needed to validate the session |
| `%NEXT_URL%` | URL to continue with remote validation of the session. |

View File

@@ -28,7 +28,7 @@ public enum UserIdType {
Localpart("localpart"),
MatrixID("mxid"),
EmailLocalpart("email_localpart"),
Email("email");
Email("threepids/email");
private String id;

View File

@@ -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());
}
}

View File

@@ -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();

View File

@@ -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() {

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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()));
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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,

View File

@@ -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) {

View File

@@ -25,7 +25,7 @@ public class SessionEmailTokenRequestJson extends GenericTokenRequestJson {
private String email;
public String getMedium() {
return "email";
return "threepids/email";
}
public String getValue() {

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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()) {

View File

@@ -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);
}

View File

@@ -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();
}
}

View File

@@ -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));
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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));
}
}

View File

@@ -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";
}
}

View File

@@ -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);
}
}

View File

@@ -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";
}
}

View File

@@ -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)) {

View File

@@ -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:

View 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!

View File

@@ -0,0 +1 @@
Your Matrix token is %VALIDATION_TOKEN%

View File

@@ -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%