Blindly accept mappings so Riot is happy - Fix #23
This commit is contained in:
@@ -57,6 +57,9 @@ dependencies {
|
|||||||
// HTTP connections
|
// HTTP connections
|
||||||
compile 'org.apache.httpcomponents:httpclient:4.5.3'
|
compile 'org.apache.httpcomponents:httpclient:4.5.3'
|
||||||
|
|
||||||
|
// JSON
|
||||||
|
compile 'com.google.code.gson:gson:2.8.1'
|
||||||
|
|
||||||
testCompile 'junit:junit:4.12'
|
testCompile 'junit:junit:4.12'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,48 +20,132 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.controller.v1
|
package io.kamax.mxisd.controller.v1
|
||||||
|
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import io.kamax.mxisd.controller.v1.io.SessionEmailTokenRequestJson
|
||||||
|
import io.kamax.mxisd.controller.v1.io.SessionPhoneTokenRequestJson
|
||||||
|
import io.kamax.mxisd.exception.BadRequestException
|
||||||
import io.kamax.mxisd.exception.NotImplementedException
|
import io.kamax.mxisd.exception.NotImplementedException
|
||||||
|
import io.kamax.mxisd.lookup.ThreePid
|
||||||
|
import io.kamax.mxisd.mapping.MappingManager
|
||||||
|
import org.apache.commons.io.IOUtils
|
||||||
|
import org.apache.commons.lang.StringUtils
|
||||||
|
import org.apache.http.HttpStatus
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
import org.springframework.web.bind.annotation.RequestMapping
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam
|
||||||
import org.springframework.web.bind.annotation.RestController
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest
|
import javax.servlet.http.HttpServletRequest
|
||||||
|
import javax.servlet.http.HttpServletResponse
|
||||||
import static org.springframework.web.bind.annotation.RequestMethod.GET
|
import java.nio.charset.StandardCharsets
|
||||||
import static org.springframework.web.bind.annotation.RequestMethod.POST
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
class SessionController {
|
class SessionController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MappingManager mgr
|
||||||
|
|
||||||
|
private Gson gson = new Gson()
|
||||||
|
|
||||||
private Logger log = LoggerFactory.getLogger(SessionController.class)
|
private Logger log = LoggerFactory.getLogger(SessionController.class)
|
||||||
|
|
||||||
@RequestMapping(value = "/_matrix/identity/api/v1/validate/{medium}/requestToken", method = POST)
|
private <T> T fromJson(HttpServletRequest req, Class<T> obj) {
|
||||||
String init(HttpServletRequest request) {
|
gson.fromJson(new InputStreamReader(req.getInputStream(), StandardCharsets.UTF_8), obj)
|
||||||
log.error("{} was requested but not implemented", request.getRequestURL())
|
|
||||||
|
|
||||||
throw new NotImplementedException()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "/_matrix/identity/api/v1/validate/{medium}/submitToken", method = [GET, POST])
|
@RequestMapping(value = "/_matrix/identity/api/v1/validate/{medium}/requestToken")
|
||||||
|
String init(HttpServletRequest request, HttpServletResponse response, @PathVariable String medium) {
|
||||||
|
log.info("Requested: {}", request.getRequestURL(), request.getQueryString())
|
||||||
|
|
||||||
|
if (StringUtils.equals("email", medium)) {
|
||||||
|
SessionEmailTokenRequestJson req = fromJson(request, SessionEmailTokenRequestJson.class)
|
||||||
|
return gson.toJson(new Sid(mgr.create(req)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.equals("msisdn", medium)) {
|
||||||
|
SessionPhoneTokenRequestJson req = fromJson(request, SessionPhoneTokenRequestJson.class)
|
||||||
|
return gson.toJson(new Sid(mgr.create(req)))
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObject obj = new JsonObject();
|
||||||
|
obj.addProperty("errcode", "M_INVALID_3PID_TYPE")
|
||||||
|
obj.addProperty("error", medium + " is not supported as a 3PID type")
|
||||||
|
response.setStatus(HttpStatus.SC_BAD_REQUEST)
|
||||||
|
return gson.toJson(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = "/_matrix/identity/api/v1/validate/{medium}/submitToken")
|
||||||
String validate(HttpServletRequest request) {
|
String validate(HttpServletRequest request) {
|
||||||
log.error("{} was requested but not implemented", request.getRequestURL())
|
log.info("Requested: {}?{}", request.getRequestURL(), request.getQueryString())
|
||||||
|
|
||||||
throw new NotImplementedException()
|
throw new NotImplementedException()
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "/_matrix/identity/api/v1/3pid/getValidated3pid", method = POST)
|
@RequestMapping(value = "/_matrix/identity/api/v1/3pid/getValidated3pid")
|
||||||
String check(HttpServletRequest request) {
|
String check(HttpServletRequest request, HttpServletResponse response,
|
||||||
log.error("{} was requested but not implemented", request.getRequestURL())
|
@RequestParam String sid, @RequestParam("client_secret") String secret) {
|
||||||
|
log.info("Requested: {}?{}", request.getRequestURL(), request.getQueryString())
|
||||||
|
|
||||||
throw new NotImplementedException()
|
Optional<ThreePid> result = mgr.getValidated(sid, secret)
|
||||||
|
if (result.isPresent()) {
|
||||||
|
log.info("requested session was validated")
|
||||||
|
ThreePid pid = result.get()
|
||||||
|
|
||||||
|
JsonObject obj = new JsonObject()
|
||||||
|
obj.addProperty("medium", pid.getMedium())
|
||||||
|
obj.addProperty("address", pid.getAddress())
|
||||||
|
obj.addProperty("validated_at", pid.getValidation().toEpochMilli())
|
||||||
|
|
||||||
|
return gson.toJson(obj);
|
||||||
|
} else {
|
||||||
|
log.info("requested session was not validated")
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "/_matrix/identity/api/v1/3pid/bind", method = POST)
|
@RequestMapping(value = "/_matrix/identity/api/v1/3pid/bind")
|
||||||
String bind(HttpServletRequest request) {
|
String bind(HttpServletRequest request, HttpServletResponse response,
|
||||||
log.error("{} was requested but not implemented", request.getRequestURL())
|
@RequestParam String sid, @RequestParam("client_secret") String secret, @RequestParam String mxid) {
|
||||||
|
String data = IOUtils.toString(request.getReader())
|
||||||
|
log.info("Requested: {}", request.getRequestURL(), request.getQueryString())
|
||||||
|
try {
|
||||||
|
mgr.bind(sid, secret, mxid)
|
||||||
|
return "{}"
|
||||||
|
} catch (BadRequestException e) {
|
||||||
|
log.info("requested session was not validated")
|
||||||
|
|
||||||
throw new NotImplementedException()
|
obj = new JsonObject()
|
||||||
|
obj.addProperty("errcode", "M_SESSION_NOT_VALIDATED")
|
||||||
|
obj.addProperty("error", e.getMessage())
|
||||||
|
response.setStatus(HttpStatus.SC_BAD_REQUEST)
|
||||||
|
return gson.toJson(obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Sid {
|
||||||
|
|
||||||
|
private String sid;
|
||||||
|
|
||||||
|
public Sid(String sid) {
|
||||||
|
setSid(sid);
|
||||||
|
}
|
||||||
|
|
||||||
|
String getSid() {
|
||||||
|
return sid
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSid(String sid) {
|
||||||
|
this.sid = sid
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package io.kamax.mxisd.controller.v1.io;
|
||||||
|
|
||||||
|
import io.kamax.mxisd.mapping.MappingSession;
|
||||||
|
|
||||||
|
public abstract class GenericTokenRequestJson implements MappingSession {
|
||||||
|
|
||||||
|
private String client_secret;
|
||||||
|
private int send_attempt;
|
||||||
|
private String id_server;
|
||||||
|
|
||||||
|
public String getSecret() {
|
||||||
|
return client_secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAttempt() {
|
||||||
|
return send_attempt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getServer() {
|
||||||
|
return id_server;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package io.kamax.mxisd.controller.v1.io;
|
||||||
|
|
||||||
|
public class SessionEmailTokenRequestJson extends GenericTokenRequestJson {
|
||||||
|
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMedium() {
|
||||||
|
return "email";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getValue() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package io.kamax.mxisd.controller.v1.io;
|
||||||
|
|
||||||
|
public class SessionPhoneTokenRequestJson extends GenericTokenRequestJson {
|
||||||
|
|
||||||
|
private String country;
|
||||||
|
private String phone_number;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMedium() {
|
||||||
|
return "email";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getValue() {
|
||||||
|
return phone_number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCountry() {
|
||||||
|
return country;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
29
src/main/groovy/io/kamax/mxisd/lookup/ThreePid.java
Normal file
29
src/main/groovy/io/kamax/mxisd/lookup/ThreePid.java
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package io.kamax.mxisd.lookup;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
public class ThreePid {
|
||||||
|
|
||||||
|
private String medium;
|
||||||
|
private String address;
|
||||||
|
private Instant validation;
|
||||||
|
|
||||||
|
public ThreePid(String medium, String address, Instant validation) {
|
||||||
|
this.medium = medium;
|
||||||
|
this.address = address;
|
||||||
|
this.validation = validation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMedium() {
|
||||||
|
return medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAddress() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instant getValidation() {
|
||||||
|
return validation;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -23,9 +23,12 @@ package io.kamax.mxisd.lookup.strategy
|
|||||||
import io.kamax.mxisd.lookup.BulkLookupRequest
|
import io.kamax.mxisd.lookup.BulkLookupRequest
|
||||||
import io.kamax.mxisd.lookup.SingleLookupRequest
|
import io.kamax.mxisd.lookup.SingleLookupRequest
|
||||||
import io.kamax.mxisd.lookup.ThreePidMapping
|
import io.kamax.mxisd.lookup.ThreePidMapping
|
||||||
|
import io.kamax.mxisd.lookup.provider.IThreePidProvider
|
||||||
|
|
||||||
interface LookupStrategy {
|
interface LookupStrategy {
|
||||||
|
|
||||||
|
List<IThreePidProvider> getLocalProviders()
|
||||||
|
|
||||||
Optional<?> find(SingleLookupRequest request)
|
Optional<?> find(SingleLookupRequest request)
|
||||||
|
|
||||||
List<ThreePidMapping> find(BulkLookupRequest requests)
|
List<ThreePidMapping> find(BulkLookupRequest requests)
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ import org.springframework.beans.factory.InitializingBean
|
|||||||
import org.springframework.beans.factory.annotation.Autowired
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
import java.util.function.Predicate
|
||||||
|
import java.util.stream.Collectors
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
class RecursivePriorityLookupStrategy implements LookupStrategy, InitializingBean {
|
class RecursivePriorityLookupStrategy implements LookupStrategy, InitializingBean {
|
||||||
|
|
||||||
@@ -104,6 +107,16 @@ class RecursivePriorityLookupStrategy implements LookupStrategy, InitializingBea
|
|||||||
return usableProviders
|
return usableProviders
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
List<IThreePidProvider> getLocalProviders() {
|
||||||
|
return providers.stream().filter(new Predicate<IThreePidProvider>() {
|
||||||
|
@Override
|
||||||
|
boolean test(IThreePidProvider iThreePidProvider) {
|
||||||
|
return iThreePidProvider.isEnabled() && iThreePidProvider.isLocal()
|
||||||
|
}
|
||||||
|
}).collect(Collectors.toList())
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Optional<?> find(SingleLookupRequest request) {
|
Optional<?> find(SingleLookupRequest request) {
|
||||||
for (IThreePidProvider provider : listUsableProviders(request)) {
|
for (IThreePidProvider provider : listUsableProviders(request)) {
|
||||||
|
|||||||
152
src/main/groovy/io/kamax/mxisd/mapping/MappingManager.java
Normal file
152
src/main/groovy/io/kamax/mxisd/mapping/MappingManager.java
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
package io.kamax.mxisd.mapping;
|
||||||
|
|
||||||
|
import io.kamax.mxisd.exception.BadRequestException;
|
||||||
|
import io.kamax.mxisd.lookup.ThreePid;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MappingManager {
|
||||||
|
|
||||||
|
private Logger log = LoggerFactory.getLogger(MappingManager.class);
|
||||||
|
|
||||||
|
private Map<String, Session> threePidLookups = new WeakHashMap<>();
|
||||||
|
private Map<String, Session> sessions = new HashMap<>();
|
||||||
|
private Timer cleaner;
|
||||||
|
|
||||||
|
MappingManager() {
|
||||||
|
cleaner = new Timer();
|
||||||
|
cleaner.schedule(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
List<Session> sList = new ArrayList<>(sessions.values());
|
||||||
|
for (Session s : sList) {
|
||||||
|
if (s.timestamp.plus(24, ChronoUnit.HOURS).isBefore(Instant.now())) { // TODO config timeout
|
||||||
|
log.info("Session {} is obsolete, removing", s.sid);
|
||||||
|
|
||||||
|
sessions.remove(s.sid);
|
||||||
|
threePidLookups.remove(s.hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 0, 10 * 1000); // TODO config delay
|
||||||
|
}
|
||||||
|
|
||||||
|
public String create(MappingSession data) {
|
||||||
|
String sid;
|
||||||
|
do {
|
||||||
|
sid = Long.toString(System.currentTimeMillis());
|
||||||
|
} while (sessions.containsKey(sid));
|
||||||
|
|
||||||
|
String threePidHash = data.getMedium() + data.getValue();
|
||||||
|
Session session = threePidLookups.get(threePidHash);
|
||||||
|
if (session != null) {
|
||||||
|
sid = session.sid;
|
||||||
|
} else {
|
||||||
|
// TODO perform some kind of validation
|
||||||
|
|
||||||
|
session = new Session(sid, threePidHash, data);
|
||||||
|
sessions.put(sid, session);
|
||||||
|
threePidLookups.put(threePidHash, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Created new session {} to validate {} {}", sid, session.medium, session.address);
|
||||||
|
return sid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<ThreePid> getValidated(String sid, String secret) {
|
||||||
|
Session s = sessions.get(sid);
|
||||||
|
if (s != null && StringUtils.equals(s.secret, secret)) {
|
||||||
|
return Optional.of(new ThreePid(s.medium, s.address, s.validationTimestamp));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bind(String sid, String secret, String mxid) {
|
||||||
|
Session s = sessions.get(sid);
|
||||||
|
if (s == null || !StringUtils.equals(s.secret, secret)) {
|
||||||
|
throw new BadRequestException("sid or secret are not valid");
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Performed bind for mxid {}", mxid);
|
||||||
|
// TODO perform bind, whatever it is
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Session {
|
||||||
|
|
||||||
|
private String sid;
|
||||||
|
private String hash;
|
||||||
|
private Instant timestamp;
|
||||||
|
private Instant validationTimestamp;
|
||||||
|
private boolean isValidated;
|
||||||
|
private String secret;
|
||||||
|
private String medium;
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
public Session(String sid, String hash, MappingSession data) {
|
||||||
|
this.sid = sid;
|
||||||
|
this.hash = hash;
|
||||||
|
timestamp = Instant.now();
|
||||||
|
validationTimestamp = Instant.now();
|
||||||
|
secret = data.getSecret();
|
||||||
|
medium = data.getMedium();
|
||||||
|
address = data.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instant getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimestamp(Instant timestamp) {
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instant getValidationTimestamp() {
|
||||||
|
return validationTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValidationTimestamp(Instant validationTimestamp) {
|
||||||
|
this.validationTimestamp = validationTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isValidated() {
|
||||||
|
return isValidated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValidated(boolean validated) {
|
||||||
|
isValidated = validated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSecret() {
|
||||||
|
return secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecret(String secret) {
|
||||||
|
this.secret = secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMedium() {
|
||||||
|
return medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMedium(String medium) {
|
||||||
|
this.medium = medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAddress() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAddress(String address) {
|
||||||
|
this.address = address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
15
src/main/groovy/io/kamax/mxisd/mapping/MappingSession.java
Normal file
15
src/main/groovy/io/kamax/mxisd/mapping/MappingSession.java
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package io.kamax.mxisd.mapping;
|
||||||
|
|
||||||
|
public interface MappingSession {
|
||||||
|
|
||||||
|
String getServer();
|
||||||
|
|
||||||
|
String getSecret();
|
||||||
|
|
||||||
|
int getAttempt();
|
||||||
|
|
||||||
|
String getMedium();
|
||||||
|
|
||||||
|
String getValue();
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user