Refactor after first tests against synapse
This commit is contained in:
@@ -20,16 +20,131 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.config;
|
package io.kamax.mxisd.config;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
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.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConfigurationProperties("session")
|
@ConfigurationProperties("session")
|
||||||
public class SessionConfig {
|
public class SessionConfig {
|
||||||
|
|
||||||
private static Logger log = LoggerFactory.getLogger(SessionConfig.class);
|
private static Logger log = LoggerFactory.getLogger(SessionConfig.class);
|
||||||
|
|
||||||
|
public static class Policy {
|
||||||
|
|
||||||
|
public static class PolicyTemplate {
|
||||||
|
|
||||||
|
public static class PolicySource {
|
||||||
|
|
||||||
|
private boolean enabled;
|
||||||
|
private boolean toLocal;
|
||||||
|
private boolean toRemote;
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean toLocal() {
|
||||||
|
return toLocal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setToLocal(boolean toLocal) {
|
||||||
|
this.toLocal = toLocal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean toRemote() {
|
||||||
|
return toRemote;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setToRemote(boolean toRemote) {
|
||||||
|
this.toRemote = toRemote;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean enabled;
|
||||||
|
private PolicySource forLocal = new PolicySource();
|
||||||
|
private PolicySource forRemote = new PolicySource();
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PolicySource getForLocal() {
|
||||||
|
return forLocal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PolicySource forLocal() {
|
||||||
|
return forLocal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PolicySource getForRemote() {
|
||||||
|
return forRemote;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PolicySource forRemote() {
|
||||||
|
return forRemote;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PolicyTemplate bind = new PolicyTemplate();
|
||||||
|
private PolicyTemplate validation = new PolicyTemplate();
|
||||||
|
|
||||||
|
public PolicyTemplate getBind() {
|
||||||
|
return bind;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBind(PolicyTemplate bind) {
|
||||||
|
this.bind = bind;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PolicyTemplate getValidation() {
|
||||||
|
return validation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValidation(PolicyTemplate validation) {
|
||||||
|
this.validation = validation;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private MatrixConfig mxCfg;
|
||||||
|
private Policy policy = new Policy();
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public SessionConfig(MatrixConfig mxCfg) {
|
||||||
|
this.mxCfg = mxCfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MatrixConfig getMatrixCfg() {
|
||||||
|
return mxCfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Policy getPolicy() {
|
||||||
|
return policy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPolicy(Policy policy) {
|
||||||
|
this.policy = policy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void build() {
|
||||||
|
log.info("--- Session config ---");
|
||||||
|
log.info("Global Policy: {}", new Gson().toJson(policy));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -23,7 +23,9 @@ package io.kamax.mxisd.controller.v1;
|
|||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import io.kamax.mxisd.exception.BadRequestException;
|
import io.kamax.mxisd.exception.BadRequestException;
|
||||||
|
import io.kamax.mxisd.exception.InternalServerError;
|
||||||
import io.kamax.mxisd.exception.MappingAlreadyExistsException;
|
import io.kamax.mxisd.exception.MappingAlreadyExistsException;
|
||||||
|
import io.kamax.mxisd.exception.MatrixException;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -33,6 +35,8 @@ import org.springframework.web.bind.MissingServletRequestParameterException;
|
|||||||
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 java.time.Instant;
|
||||||
|
|
||||||
@ControllerAdvice
|
@ControllerAdvice
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@@ -50,6 +54,23 @@ public class DefaultExceptionHandler {
|
|||||||
return gson.toJson(obj);
|
return gson.toJson(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(InternalServerError.class)
|
||||||
|
public String handle(InternalServerError e, HttpServletResponse response) {
|
||||||
|
if (StringUtils.isNotBlank(e.getInternalReason())) {
|
||||||
|
log.error("Reference #{} - {}", e.getReference(), e.getInternalReason());
|
||||||
|
} else {
|
||||||
|
log.error("Reference #{}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return handleGeneric(e, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(MatrixException.class)
|
||||||
|
public String handleGeneric(MatrixException e, HttpServletResponse response) {
|
||||||
|
response.setStatus(e.getStatus());
|
||||||
|
return handle(e.getErrorCode(), e.getError());
|
||||||
|
}
|
||||||
|
|
||||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||||
@ExceptionHandler(MissingServletRequestParameterException.class)
|
@ExceptionHandler(MissingServletRequestParameterException.class)
|
||||||
public String handle(MissingServletRequestParameterException e) {
|
public String handle(MissingServletRequestParameterException e) {
|
||||||
@@ -72,7 +93,14 @@ public class DefaultExceptionHandler {
|
|||||||
@ExceptionHandler(RuntimeException.class)
|
@ExceptionHandler(RuntimeException.class)
|
||||||
public String handle(HttpServletRequest req, RuntimeException e) {
|
public String handle(HttpServletRequest req, RuntimeException e) {
|
||||||
log.error("Unknown error when handling {}", req.getRequestURL(), e);
|
log.error("Unknown error when handling {}", req.getRequestURL(), e);
|
||||||
return handle("M_UNKNOWN", StringUtils.defaultIfBlank(e.getMessage(), "An uknown error occured. Contact the server administrator if this persists."));
|
return handle(
|
||||||
|
"M_UNKNOWN",
|
||||||
|
StringUtils.defaultIfBlank(
|
||||||
|
e.getMessage(),
|
||||||
|
"An internal server error occured. If this error persists, please contact support with reference #" +
|
||||||
|
Instant.now().toEpochMilli()
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -27,6 +27,7 @@ import io.kamax.mxisd.ThreePid
|
|||||||
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.exception.BadRequestException
|
import io.kamax.mxisd.exception.BadRequestException
|
||||||
|
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
|
||||||
@@ -105,12 +106,10 @@ class SessionController {
|
|||||||
@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) {
|
||||||
log.info("Requested: {}?{}", request.getRequestURL(), request.getQueryString())
|
log.info("Requested: {}", request.getRequestURL(), request.getQueryString())
|
||||||
|
|
||||||
Optional<ThreePidValidation> result = mgr.getValidated(sid, secret)
|
try {
|
||||||
if (result.isPresent()) {
|
ThreePidValidation pid = mgr.getValidated(sid, secret)
|
||||||
log.info("requested session was validated")
|
|
||||||
ThreePidValidation pid = result.get()
|
|
||||||
|
|
||||||
JsonObject obj = new JsonObject()
|
JsonObject obj = new JsonObject()
|
||||||
obj.addProperty("medium", pid.getMedium())
|
obj.addProperty("medium", pid.getMedium())
|
||||||
@@ -118,14 +117,9 @@ class SessionController {
|
|||||||
obj.addProperty("validated_at", pid.getValidation().toEpochMilli())
|
obj.addProperty("validated_at", pid.getValidation().toEpochMilli())
|
||||||
|
|
||||||
return gson.toJson(obj);
|
return gson.toJson(obj);
|
||||||
} else {
|
} catch (SessionNotValidatedException e) {
|
||||||
log.info("requested session was not validated")
|
log.info("Session {} was requested but has not yet been validated", sid);
|
||||||
|
throw e;
|
||||||
JsonObject obj = new JsonObject()
|
|
||||||
obj.addProperty("errcode", "M_SESSION_NOT_VALIDATED")
|
|
||||||
obj.addProperty("error", "sid, secret or session not valid")
|
|
||||||
response.setStatus(HttpStatus.SC_BAD_REQUEST)
|
|
||||||
return gson.toJson(obj)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -20,16 +20,35 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.exception;
|
package io.kamax.mxisd.exception;
|
||||||
|
|
||||||
import org.springframework.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
|
public class InternalServerError extends MatrixException {
|
||||||
public class InternalServerError extends RuntimeException {
|
|
||||||
|
private String reference = Long.toString(Instant.now().toEpochMilli());
|
||||||
|
private String internalReason;
|
||||||
|
|
||||||
public InternalServerError() {
|
public InternalServerError() {
|
||||||
super("An internal server error occured. If this error persists, please contact support with reference #" + Instant.now().toEpochMilli());
|
super(
|
||||||
|
HttpStatus.SC_INTERNAL_SERVER_ERROR,
|
||||||
|
"M_UNKNOWN",
|
||||||
|
"An internal server error occured. If this error persists, please contact support with reference #" +
|
||||||
|
Instant.now().toEpochMilli()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InternalServerError(String internalReason) {
|
||||||
|
this();
|
||||||
|
this.internalReason = internalReason;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReference() {
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInternalReason() {
|
||||||
|
return internalReason;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
public abstract class MatrixException extends MxisdException {
|
||||||
|
|
||||||
|
private int status;
|
||||||
|
private String errorCode;
|
||||||
|
private String error;
|
||||||
|
|
||||||
|
public MatrixException(int status, String errorCode, String error) {
|
||||||
|
this.status = status;
|
||||||
|
this.errorCode = errorCode;
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getErrorCode() {
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getError() {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
24
src/main/groovy/io/kamax/mxisd/exception/MxisdException.java
Normal file
24
src/main/groovy/io/kamax/mxisd/exception/MxisdException.java
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
public class MxisdException extends RuntimeException {
|
||||||
|
}
|
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* 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.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
|
||||||
|
@ResponseStatus(value = HttpStatus.FORBIDDEN)
|
||||||
|
public class NotAllowedException extends RuntimeException {
|
||||||
|
|
||||||
|
public NotAllowedException(String s) {
|
||||||
|
super(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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 SessionNotValidatedException extends MatrixException {
|
||||||
|
|
||||||
|
public SessionNotValidatedException() {
|
||||||
|
super(HttpStatus.SC_OK, "M_SESSION_NOT_VALIDATED", "This validation session has not yet been completed");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -27,8 +27,8 @@ public interface INotificationHandler {
|
|||||||
|
|
||||||
String getMedium();
|
String getMedium();
|
||||||
|
|
||||||
void notify(IThreePidInviteReply invite);
|
void sendForInvite(IThreePidInviteReply invite);
|
||||||
|
|
||||||
void notify(IThreePidSession session);
|
void sendForValidation(IThreePidSession session);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -55,14 +55,14 @@ public class NotificationManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void sendForInvite(IThreePidInviteReply invite) {
|
public void sendForInvite(IThreePidInviteReply invite) {
|
||||||
ensureMedium(invite.getInvite().getMedium()).notify(invite);
|
ensureMedium(invite.getInvite().getMedium()).sendForInvite(invite);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendForValidation(IThreePidSession session) {
|
public void sendForValidation(IThreePidSession session) {
|
||||||
ensureMedium(session.getThreePid().getMedium()).notify(session);
|
ensureMedium(session.getThreePid().getMedium()).sendForValidation(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendforRemotePublish(IThreePidSession session) {
|
public void sendforRemoteValidation(IThreePidSession session) {
|
||||||
throw new NotImplementedException("Remote publish of 3PID bind");
|
throw new NotImplementedException("Remote publish of 3PID bind");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -22,9 +22,11 @@ package io.kamax.mxisd.session;
|
|||||||
|
|
||||||
import io.kamax.matrix.ThreePidMedium;
|
import io.kamax.matrix.ThreePidMedium;
|
||||||
import io.kamax.mxisd.ThreePid;
|
import io.kamax.mxisd.ThreePid;
|
||||||
|
import io.kamax.mxisd.config.MatrixConfig;
|
||||||
import io.kamax.mxisd.config.SessionConfig;
|
import io.kamax.mxisd.config.SessionConfig;
|
||||||
import io.kamax.mxisd.exception.InvalidCredentialsException;
|
import io.kamax.mxisd.exception.InvalidCredentialsException;
|
||||||
import io.kamax.mxisd.lookup.SingleLookupReply;
|
import io.kamax.mxisd.exception.NotAllowedException;
|
||||||
|
import io.kamax.mxisd.exception.SessionNotValidatedException;
|
||||||
import io.kamax.mxisd.lookup.ThreePidValidation;
|
import io.kamax.mxisd.lookup.ThreePidValidation;
|
||||||
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
|
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
|
||||||
import io.kamax.mxisd.notification.NotificationManager;
|
import io.kamax.mxisd.notification.NotificationManager;
|
||||||
@@ -39,7 +41,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class SessionMananger {
|
public class SessionMananger {
|
||||||
@@ -52,13 +53,26 @@ public class SessionMananger {
|
|||||||
private NotificationManager notifMgr;
|
private NotificationManager notifMgr;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public SessionMananger(SessionConfig cfg, IStorage storage, LookupStrategy lookup, NotificationManager notifMgr) {
|
public SessionMananger(SessionConfig cfg, MatrixConfig mxCfg, IStorage storage, LookupStrategy lookup, NotificationManager notifMgr) {
|
||||||
this.cfg = cfg;
|
this.cfg = cfg;
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
this.lookup = lookup;
|
this.lookup = lookup;
|
||||||
this.notifMgr = notifMgr;
|
this.notifMgr = notifMgr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isLocal(ThreePid tpid) {
|
||||||
|
if (!ThreePidMedium.Email.is(tpid.getMedium())) { // We can only handle E-mails for now
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String domain = tpid.getAddress().split("@")[1];
|
||||||
|
return StringUtils.equalsIgnoreCase(cfg.getMatrixCfg().getDomain(), domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isKnownLocal(ThreePid tpid) {
|
||||||
|
return lookup.findLocal(tpid.getMedium(), tpid.getAddress()).isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
private ThreePidSession getSession(String sid, String secret) {
|
private ThreePidSession getSession(String sid, String secret) {
|
||||||
Optional<IThreePidSessionDao> dao = storage.getThreePidSession(sid);
|
Optional<IThreePidSessionDao> dao = storage.getThreePidSession(sid);
|
||||||
if (!dao.isPresent() || !StringUtils.equals(dao.get().getSecret(), secret)) {
|
if (!dao.isPresent() || !StringUtils.equals(dao.get().getSecret(), secret)) {
|
||||||
@@ -71,12 +85,17 @@ public class SessionMananger {
|
|||||||
private ThreePidSession getSessionIfValidated(String sid, String secret) {
|
private ThreePidSession getSessionIfValidated(String sid, String secret) {
|
||||||
ThreePidSession session = getSession(sid, secret);
|
ThreePidSession session = getSession(sid, secret);
|
||||||
if (!session.isValidated()) {
|
if (!session.isValidated()) {
|
||||||
throw new IllegalStateException("Session " + sid + " has not been validated");
|
throw new SessionNotValidatedException();
|
||||||
}
|
}
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String create(String server, ThreePid tpid, String secret, int attempt, String nextLink) {
|
public String create(String server, ThreePid tpid, String secret, int attempt, String nextLink) {
|
||||||
|
SessionConfig.Policy.PolicyTemplate policy = cfg.getPolicy().getValidation();
|
||||||
|
if (!policy.isEnabled()) {
|
||||||
|
throw new NotAllowedException("Validating 3PID is disabled globally");
|
||||||
|
}
|
||||||
|
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
log.info("Server {} is asking to create session for {} (Attempt #{}) - Next link: {}", server, tpid, attempt, nextLink);
|
log.info("Server {} is asking to create session for {} (Attempt #{}) - Next link: {}", server, tpid, attempt, nextLink);
|
||||||
Optional<IThreePidSessionDao> dao = storage.findThreePidSession(tpid, secret);
|
Optional<IThreePidSessionDao> dao = storage.findThreePidSession(tpid, secret);
|
||||||
@@ -94,17 +113,46 @@ public class SessionMananger {
|
|||||||
return session.getId();
|
return session.getId();
|
||||||
} else {
|
} else {
|
||||||
log.info("No existing session for {}", tpid);
|
log.info("No existing session for {}", tpid);
|
||||||
|
|
||||||
|
boolean isLocalDomain = isLocal(tpid);
|
||||||
|
log.info("Is 3PID bound to local domain? {}", isLocalDomain);
|
||||||
|
|
||||||
|
if (isLocalDomain && (!policy.forLocal().isEnabled() || !policy.forLocal().toLocal())) {
|
||||||
|
throw new NotAllowedException("Validating local 3PID is not allowed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// We lookup if the 3PID is already known locally.
|
||||||
|
boolean knownLocal = isKnownLocal(tpid);
|
||||||
|
log.info("Mapping with {} is " + (knownLocal ? "already" : "not") + " known locally", tpid);
|
||||||
|
|
||||||
|
if (!isLocalDomain && (
|
||||||
|
!policy.forRemote().isEnabled() || (
|
||||||
|
!policy.forRemote().toLocal() &&
|
||||||
|
!policy.forRemote().toRemote()
|
||||||
|
)
|
||||||
|
)) {
|
||||||
|
throw new NotAllowedException("Validating unknown remote 3PID is not allowed");
|
||||||
|
}
|
||||||
|
|
||||||
String sessionId;
|
String sessionId;
|
||||||
do {
|
do {
|
||||||
sessionId = UUID.randomUUID().toString().replace("-", "");
|
sessionId = Long.toString(System.currentTimeMillis());
|
||||||
} while (storage.getThreePidSession(sessionId).isPresent());
|
} while (storage.getThreePidSession(sessionId).isPresent());
|
||||||
|
|
||||||
String token = RandomStringUtils.randomNumeric(6);
|
String token = RandomStringUtils.randomNumeric(6);
|
||||||
ThreePidSession session = new ThreePidSession(sessionId, server, tpid, secret, attempt, nextLink, token);
|
ThreePidSession session = new ThreePidSession(sessionId, server, tpid, secret, attempt, nextLink, token);
|
||||||
log.info("Generated new session {} to validate {} from server {}", sessionId, tpid, server);
|
log.info("Generated new session {} to validate {} from server {}", sessionId, tpid, server);
|
||||||
|
|
||||||
notifMgr.sendForValidation(session);
|
// This might need a configuration by medium type?
|
||||||
log.info("Sent validation notification to {}", tpid);
|
if (!isLocalDomain) {
|
||||||
|
if (policy.forRemote().toLocal() && policy.forRemote().toRemote()) {
|
||||||
|
log.info("Session {} for {}: sending local validation notification", sessionId, tpid);
|
||||||
|
notifMgr.sendForValidation(session);
|
||||||
|
} else {
|
||||||
|
log.info("Session {} for {}: sending remote-only validation notification", sessionId, tpid);
|
||||||
|
notifMgr.sendforRemoteValidation(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
storage.insertThreePidSession(session.getDao());
|
storage.insertThreePidSession(session.getDao());
|
||||||
log.info("Stored session {}", sessionId, tpid, server);
|
log.info("Stored session {}", sessionId, tpid, server);
|
||||||
@@ -130,65 +178,8 @@ public class SessionMananger {
|
|||||||
|
|
||||||
public void bind(String sid, String secret, String mxid) {
|
public void bind(String sid, String secret, String mxid) {
|
||||||
ThreePidSession session = getSessionIfValidated(sid, secret);
|
ThreePidSession session = getSessionIfValidated(sid, secret);
|
||||||
log.info("Attempting bind of {} on session {} from server {}", mxid, session.getId(), session.getServer());
|
log.info("Accepting bind of {} on session {} from server {}", mxid, session.getId(), session.getServer());
|
||||||
|
// TODO perform this if request was proxied
|
||||||
// We lookup if the 3PID is already known remotely.
|
|
||||||
Optional<SingleLookupReply> rRemote = lookup.findRemote(session.getThreePid().getMedium(), session.getThreePid().getAddress());
|
|
||||||
boolean knownRemote = rRemote.isPresent() && StringUtils.equals(rRemote.get().getMxid().getId(), mxid);
|
|
||||||
log.info("Mapping {} -> {} is " + (knownRemote ? "already" : "not") + " known remotely", mxid, session.getThreePid());
|
|
||||||
|
|
||||||
boolean isLocalDomain = false;
|
|
||||||
if (ThreePidMedium.Email.is(session.getThreePid().getMedium())) {
|
|
||||||
// TODO
|
|
||||||
// 1. Extract domain from email
|
|
||||||
// 2. set isLocalDomain
|
|
||||||
isLocalDomain = session.getThreePid().getAddress().isEmpty(); // FIXME only for testing
|
|
||||||
}
|
|
||||||
if (knownRemote) {
|
|
||||||
log.info("No further action needed for Mapping {} -> {}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We lookup if the 3PID is already known locally.
|
|
||||||
Optional<SingleLookupReply> rLocal = lookup.findLocal(session.getThreePid().getMedium(), session.getThreePid().getAddress());
|
|
||||||
boolean knownLocal = rLocal.isPresent() && StringUtils.equals(rLocal.get().getMxid().getId(), mxid);
|
|
||||||
log.info("Mapping {} -> {} is " + (knownLocal ? "already" : "not") + " known locally", mxid, session.getThreePid());
|
|
||||||
|
|
||||||
// This might need a configuration by medium type?
|
|
||||||
if (knownLocal) { // 3PID is ony known local
|
|
||||||
if (isLocalDomain) {
|
|
||||||
// TODO
|
|
||||||
// 1. Check if global publishing is enabled, allowed and offered. If one is no, return.
|
|
||||||
// 2. Publish globally
|
|
||||||
notifMgr.sendforRemotePublish(session);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (System.currentTimeMillis() % 2 == 0) { // FIXME only for testing
|
|
||||||
// TODO
|
|
||||||
// 1. Check if configured to publish globally non-local domain. If no, return
|
|
||||||
notifMgr.sendforRemotePublish(session);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
// Proxy to configurable IS, by default Matrix.org
|
|
||||||
//
|
|
||||||
// Separate workflow, if user accepts to publish globally
|
|
||||||
// 1. display page to the user that it is waiting for the confirmation
|
|
||||||
// 2. call mxisd-specific endpoint to publish globally
|
|
||||||
// 3. check regularly on client page for a binding
|
|
||||||
// 4. when found, show page "Done globally!"
|
|
||||||
notifMgr.sendforRemotePublish(session);
|
|
||||||
} else {
|
|
||||||
if (isLocalDomain) { // 3PID is not known anywhere but is a local domain
|
|
||||||
// TODO
|
|
||||||
// check if config says this should fail or silently accept.
|
|
||||||
// Required to silently accept if the backend is synapse itself.
|
|
||||||
} else { // 3PID is not known anywhere and is remote
|
|
||||||
// TODO
|
|
||||||
// Proxy to configurable IS, by default Matrix.org
|
|
||||||
notifMgr.sendforRemotePublish(session);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -24,6 +24,8 @@ public interface IThreePidSessionDao {
|
|||||||
|
|
||||||
String getId();
|
String getId();
|
||||||
|
|
||||||
|
long getCreationTime();
|
||||||
|
|
||||||
String getServer();
|
String getServer();
|
||||||
|
|
||||||
String getMedium();
|
String getMedium();
|
||||||
@@ -38,4 +40,8 @@ public interface IThreePidSessionDao {
|
|||||||
|
|
||||||
String getToken();
|
String getToken();
|
||||||
|
|
||||||
|
boolean getValidated();
|
||||||
|
|
||||||
|
long getValidationTime();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -31,6 +31,9 @@ public class ThreePidSessionDao implements IThreePidSessionDao {
|
|||||||
@DatabaseField(id = true)
|
@DatabaseField(id = true)
|
||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
|
@DatabaseField(canBeNull = false)
|
||||||
|
private long creationTime;
|
||||||
|
|
||||||
@DatabaseField(canBeNull = false)
|
@DatabaseField(canBeNull = false)
|
||||||
private String server;
|
private String server;
|
||||||
|
|
||||||
@@ -52,12 +55,19 @@ public class ThreePidSessionDao implements IThreePidSessionDao {
|
|||||||
@DatabaseField(canBeNull = false)
|
@DatabaseField(canBeNull = false)
|
||||||
private String token;
|
private String token;
|
||||||
|
|
||||||
|
@DatabaseField
|
||||||
|
private boolean validated;
|
||||||
|
|
||||||
|
@DatabaseField
|
||||||
|
private long validationTime;
|
||||||
|
|
||||||
public ThreePidSessionDao() {
|
public ThreePidSessionDao() {
|
||||||
// stub for ORMLite
|
// stub for ORMLite
|
||||||
}
|
}
|
||||||
|
|
||||||
public ThreePidSessionDao(IThreePidSessionDao session) {
|
public ThreePidSessionDao(IThreePidSessionDao session) {
|
||||||
setId(session.getId());
|
setId(session.getId());
|
||||||
|
setCreationTime(session.getCreationTime());
|
||||||
setServer(session.getServer());
|
setServer(session.getServer());
|
||||||
setMedium(session.getMedium());
|
setMedium(session.getMedium());
|
||||||
setAddress(session.getAddress());
|
setAddress(session.getAddress());
|
||||||
@@ -65,6 +75,9 @@ public class ThreePidSessionDao implements IThreePidSessionDao {
|
|||||||
setAttempt(session.getAttempt());
|
setAttempt(session.getAttempt());
|
||||||
setNextLink(session.getNextLink());
|
setNextLink(session.getNextLink());
|
||||||
setToken(session.getToken());
|
setToken(session.getToken());
|
||||||
|
setValidated(session.getValidated());
|
||||||
|
setValidationTime(session.getValidationTime());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ThreePidSessionDao(ThreePid tpid, String secret) {
|
public ThreePidSessionDao(ThreePid tpid, String secret) {
|
||||||
@@ -82,6 +95,15 @@ public class ThreePidSessionDao implements IThreePidSessionDao {
|
|||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getCreationTime() {
|
||||||
|
return creationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreationTime(long creationTime) {
|
||||||
|
this.creationTime = creationTime;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getServer() {
|
public String getServer() {
|
||||||
return server;
|
return server;
|
||||||
@@ -91,24 +113,6 @@ public class ThreePidSessionDao implements IThreePidSessionDao {
|
|||||||
this.server = server;
|
this.server = server;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getSecret() {
|
|
||||||
return secret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSecret(String secret) {
|
|
||||||
this.secret = secret;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getAttempt() {
|
|
||||||
return attempt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAttempt(int attempt) {
|
|
||||||
this.attempt = attempt;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMedium() {
|
public String getMedium() {
|
||||||
return medium;
|
return medium;
|
||||||
@@ -127,6 +131,24 @@ public class ThreePidSessionDao implements IThreePidSessionDao {
|
|||||||
this.address = address;
|
this.address = address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSecret() {
|
||||||
|
return secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecret(String secret) {
|
||||||
|
this.secret = secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAttempt() {
|
||||||
|
return attempt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttempt(int attempt) {
|
||||||
|
this.attempt = attempt;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getNextLink() {
|
public String getNextLink() {
|
||||||
return nextLink;
|
return nextLink;
|
||||||
@@ -144,4 +166,22 @@ public class ThreePidSessionDao implements IThreePidSessionDao {
|
|||||||
public void setToken(String token) {
|
public void setToken(String token) {
|
||||||
this.token = token;
|
this.token = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean getValidated() {
|
||||||
|
return validated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValidated(boolean validated) {
|
||||||
|
this.validated = validated;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getValidationTime() {
|
||||||
|
return validationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValidationTime(long validationTime) {
|
||||||
|
this.validationTime = validationTime;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -23,7 +23,9 @@ package io.kamax.mxisd.threepid.connector.email;
|
|||||||
import com.sun.mail.smtp.SMTPTransport;
|
import com.sun.mail.smtp.SMTPTransport;
|
||||||
import io.kamax.matrix.ThreePidMedium;
|
import io.kamax.matrix.ThreePidMedium;
|
||||||
import io.kamax.mxisd.config.threepid.connector.EmailSmtpConfig;
|
import io.kamax.mxisd.config.threepid.connector.EmailSmtpConfig;
|
||||||
|
import io.kamax.mxisd.exception.InternalServerError;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
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.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -64,6 +66,10 @@ public class EmailSmtpConnector implements IEmailConnector {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void send(String senderAddress, String senderName, String recipient, String content) {
|
public void send(String senderAddress, String senderName, String recipient, String content) {
|
||||||
|
if (StringUtils.isBlank(content)) {
|
||||||
|
throw new InternalServerError("Notification content is empty");
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
InternetAddress sender = new InternetAddress(senderAddress, senderName);
|
InternetAddress sender = new InternetAddress(senderAddress, senderName);
|
||||||
MimeMessage msg = new MimeMessage(session, IOUtils.toInputStream(content, StandardCharsets.UTF_8));
|
MimeMessage msg = new MimeMessage(session, IOUtils.toInputStream(content, StandardCharsets.UTF_8));
|
||||||
|
@@ -29,7 +29,7 @@ public interface INotificationGenerator {
|
|||||||
|
|
||||||
String getMedium();
|
String getMedium();
|
||||||
|
|
||||||
String get(IThreePidInviteReply invite);
|
String getForInvite(IThreePidInviteReply invite);
|
||||||
|
|
||||||
String getForValidation(IThreePidSession session);
|
String getForValidation(IThreePidSession session);
|
||||||
|
|
||||||
|
@@ -22,13 +22,19 @@ package io.kamax.mxisd.threepid.notification.email;
|
|||||||
|
|
||||||
import io.kamax.mxisd.ThreePid;
|
import io.kamax.mxisd.ThreePid;
|
||||||
import io.kamax.mxisd.config.MatrixConfig;
|
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.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.exception.InternalServerError;
|
||||||
|
import io.kamax.mxisd.exception.NotImplementedException;
|
||||||
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
||||||
import io.kamax.mxisd.threepid.session.IThreePidSession;
|
import io.kamax.mxisd.threepid.session.IThreePidSession;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.apache.commons.lang.WordUtils;
|
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.context.ApplicationContext;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@@ -41,17 +47,22 @@ import java.nio.charset.StandardCharsets;
|
|||||||
@Component
|
@Component
|
||||||
public class EmailNotificationGenerator implements IEmailNotificationGenerator {
|
public class EmailNotificationGenerator implements IEmailNotificationGenerator {
|
||||||
|
|
||||||
|
private Logger log = LoggerFactory.getLogger(EmailNotificationGenerator.class);
|
||||||
|
|
||||||
private EmailConfig cfg;
|
private EmailConfig cfg;
|
||||||
private EmailTemplateConfig templateCfg;
|
private EmailTemplateConfig templateCfg;
|
||||||
private MatrixConfig mxCfg;
|
private MatrixConfig mxCfg;
|
||||||
|
private ServerConfig srvCfg;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
private ApplicationContext app;
|
private ApplicationContext app;
|
||||||
|
|
||||||
@Autowired // FIXME ApplicationContext shouldn't be injected, find another way from config (?)
|
@Autowired
|
||||||
public EmailNotificationGenerator(EmailConfig cfg, EmailTemplateConfig templateCfg, MatrixConfig mxCfg, ApplicationContext app) {
|
public EmailNotificationGenerator(EmailTemplateConfig templateCfg, EmailConfig cfg, MatrixConfig mxCfg, ServerConfig srvCfg) {
|
||||||
this.cfg = cfg;
|
this.cfg = cfg;
|
||||||
this.templateCfg = templateCfg;
|
this.templateCfg = templateCfg;
|
||||||
this.mxCfg = mxCfg;
|
this.mxCfg = mxCfg;
|
||||||
this.app = app;
|
this.srvCfg = srvCfg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -59,10 +70,14 @@ public class EmailNotificationGenerator implements IEmailNotificationGenerator {
|
|||||||
return "template";
|
return "template";
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getTemplateContent(String location) throws IOException {
|
private String getTemplateContent(String location) {
|
||||||
InputStream is = StringUtils.startsWith(location, "classpath:") ?
|
try {
|
||||||
app.getResource(location).getInputStream() : new FileInputStream(location);
|
InputStream is = StringUtils.startsWith(location, "classpath:") ?
|
||||||
return IOUtils.toString(is, StandardCharsets.UTF_8);
|
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) {
|
private String populateCommon(String content, ThreePid recipient) {
|
||||||
@@ -77,44 +92,51 @@ public class EmailNotificationGenerator implements IEmailNotificationGenerator {
|
|||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getTemplateAndPopulate(String location, ThreePid recipient) throws IOException {
|
private String getTemplateAndPopulate(String location, ThreePid recipient) {
|
||||||
return populateCommon(getTemplateContent(location), recipient);
|
return populateCommon(getTemplateContent(location), recipient);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String get(IThreePidInviteReply invite) {
|
public String getForInvite(IThreePidInviteReply invite) {
|
||||||
try {
|
ThreePid tpid = new ThreePid(invite.getInvite().getMedium(), invite.getInvite().getAddress());
|
||||||
ThreePid tpid = new ThreePid(invite.getInvite().getMedium(), invite.getInvite().getAddress());
|
String templateBody = getTemplateAndPopulate(templateCfg.getInvite(), tpid);
|
||||||
String templateBody = getTemplateAndPopulate(templateCfg.getInvite(), tpid);
|
|
||||||
|
|
||||||
String senderName = invite.getInvite().getProperties().getOrDefault("sender_display_name", "");
|
String senderName = invite.getInvite().getProperties().getOrDefault("sender_display_name", "");
|
||||||
String senderNameOrId = StringUtils.defaultIfBlank(senderName, invite.getInvite().getSender().getId());
|
String senderNameOrId = StringUtils.defaultIfBlank(senderName, invite.getInvite().getSender().getId());
|
||||||
String roomName = invite.getInvite().getProperties().getOrDefault("room_name", "");
|
String roomName = invite.getInvite().getProperties().getOrDefault("room_name", "");
|
||||||
String roomNameOrId = StringUtils.defaultIfBlank(roomName, invite.getInvite().getRoomId());
|
String roomNameOrId = StringUtils.defaultIfBlank(roomName, invite.getInvite().getRoomId());
|
||||||
|
|
||||||
templateBody = templateBody.replace("%SENDER_ID%", invite.getInvite().getSender().getId());
|
templateBody = templateBody.replace("%SENDER_ID%", invite.getInvite().getSender().getId());
|
||||||
templateBody = templateBody.replace("%SENDER_NAME%", senderName);
|
templateBody = templateBody.replace("%SENDER_NAME%", senderName);
|
||||||
templateBody = templateBody.replace("%SENDER_NAME_OR_ID%", senderNameOrId);
|
templateBody = templateBody.replace("%SENDER_NAME_OR_ID%", senderNameOrId);
|
||||||
templateBody = templateBody.replace("%INVITE_MEDIUM%", tpid.getMedium());
|
templateBody = templateBody.replace("%INVITE_MEDIUM%", tpid.getMedium());
|
||||||
templateBody = templateBody.replace("%INVITE_ADDRESS%", tpid.getAddress());
|
templateBody = templateBody.replace("%INVITE_ADDRESS%", tpid.getAddress());
|
||||||
templateBody = templateBody.replace("%ROOM_ID%", invite.getInvite().getRoomId());
|
templateBody = templateBody.replace("%ROOM_ID%", invite.getInvite().getRoomId());
|
||||||
templateBody = templateBody.replace("%ROOM_NAME%", roomName);
|
templateBody = templateBody.replace("%ROOM_NAME%", roomName);
|
||||||
templateBody = templateBody.replace("%ROOM_NAME_OR_ID%", roomNameOrId);
|
templateBody = templateBody.replace("%ROOM_NAME_OR_ID%", roomNameOrId);
|
||||||
|
|
||||||
return templateBody;
|
return templateBody;
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException("Unable to read template file", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getForValidation(IThreePidSession session) {
|
public String getForValidation(IThreePidSession session) {
|
||||||
return null;
|
log.info("Generating notification content for 3PID Session validation");
|
||||||
|
String templateBody = getTemplateAndPopulate(templateCfg.getSession().getValidation(), session.getThreePid());
|
||||||
|
|
||||||
|
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
|
@Override
|
||||||
public String getForRemotePublishingValidation(IThreePidSession session) {
|
public String getForRemotePublishingValidation(IThreePidSession session) {
|
||||||
return null;
|
throw new NotImplementedException("");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -42,6 +42,7 @@ public class EmailNotificationHandler implements INotificationHandler {
|
|||||||
|
|
||||||
@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;
|
||||||
generator = generators.stream()
|
generator = generators.stream()
|
||||||
.filter(o -> StringUtils.equals(cfg.getGenerator(), o.getId()))
|
.filter(o -> StringUtils.equals(cfg.getGenerator(), o.getId()))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
@@ -59,13 +60,23 @@ public class EmailNotificationHandler implements INotificationHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void notify(IThreePidInviteReply invite) {
|
public void sendForInvite(IThreePidInviteReply invite) {
|
||||||
|
connector.send(
|
||||||
|
cfg.getIdentity().getFrom(),
|
||||||
|
cfg.getIdentity().getName(),
|
||||||
|
invite.getInvite().getAddress(),
|
||||||
|
generator.getForInvite(invite)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void notify(IThreePidSession session) {
|
public void sendForValidation(IThreePidSession session) {
|
||||||
|
connector.send(
|
||||||
|
cfg.getIdentity().getFrom(),
|
||||||
|
cfg.getIdentity().getName(),
|
||||||
|
session.getThreePid().getAddress(),
|
||||||
|
generator.getForValidation(session)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -35,6 +35,8 @@ public interface IThreePidSession {
|
|||||||
|
|
||||||
ThreePid getThreePid();
|
ThreePid getThreePid();
|
||||||
|
|
||||||
|
String getSecret();
|
||||||
|
|
||||||
int getAttempt();
|
int getAttempt();
|
||||||
|
|
||||||
void increaseAttempt();
|
void increaseAttempt();
|
||||||
|
@@ -53,6 +53,11 @@ public class ThreePidSession implements IThreePidSession {
|
|||||||
dao.getNextLink(),
|
dao.getNextLink(),
|
||||||
dao.getToken()
|
dao.getToken()
|
||||||
);
|
);
|
||||||
|
timestamp = Instant.ofEpochMilli(dao.getCreationTime());
|
||||||
|
isValidated = dao.getValidated();
|
||||||
|
if (isValidated) {
|
||||||
|
validationTimestamp = Instant.ofEpochMilli(dao.getValidationTime());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ThreePidSession(String id, String server, ThreePid tPid, String secret, int attempt, String nextLink, String token) {
|
public ThreePidSession(String id, String server, ThreePid tPid, String secret, int attempt, String nextLink, String token) {
|
||||||
@@ -154,6 +159,11 @@ public class ThreePidSession implements IThreePidSession {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getCreationTime() {
|
||||||
|
return timestamp.toEpochMilli();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getServer() {
|
public String getServer() {
|
||||||
return server;
|
return server;
|
||||||
@@ -189,6 +199,16 @@ public class ThreePidSession implements IThreePidSession {
|
|||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getValidated() {
|
||||||
|
return isValidated;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getValidationTime() {
|
||||||
|
return isValidated ? validationTimestamp.toEpochMilli() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -83,6 +83,17 @@ threepid:
|
|||||||
session:
|
session:
|
||||||
validation: 'classpath:email/validate-template.eml'
|
validation: 'classpath:email/validate-template.eml'
|
||||||
|
|
||||||
|
session.policy.validation:
|
||||||
|
enabled: true
|
||||||
|
forLocal:
|
||||||
|
enabled: true
|
||||||
|
toLocal: true
|
||||||
|
toRemote: true
|
||||||
|
forRemote:
|
||||||
|
enabled: true
|
||||||
|
toLocal: true # This should not be changed unless you know exactly the implications!
|
||||||
|
toRemote: true
|
||||||
|
|
||||||
storage:
|
storage:
|
||||||
backend: 'sqlite'
|
backend: 'sqlite'
|
||||||
|
|
||||||
|
@@ -45,7 +45,7 @@ body {
|
|||||||
If this was you who made this request, you may use the following link to
|
If this was you who made this request, you may use the following link to
|
||||||
complete the verification of your email address:</p>
|
complete the verification of your email address:</p>
|
||||||
|
|
||||||
<p><a href=%VALIDATION_LINK%">Complete email verification</a></p>
|
<p><a href="%VALIDATION_LINK%">Complete email verification</a></p>
|
||||||
|
|
||||||
<p>...or copy this link into your web browser:</p>
|
<p>...or copy this link into your web browser:</p>
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user