Bye bye Groovy, you won't be missed :(

This commit is contained in:
Maxime Dor
2017-09-25 02:31:31 +02:00
parent af19fed6e7
commit 33263d3cff
140 changed files with 1711 additions and 1678 deletions

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.controller.v1;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.kamax.mxisd.auth.AuthManager;
import io.kamax.mxisd.auth.UserAuthResult;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@RestController
@CrossOrigin
@RequestMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class AuthController {
private Logger log = LoggerFactory.getLogger(AuthController.class);
private Gson gson = new Gson();
@Autowired
private AuthManager mgr;
@RequestMapping(value = "/_matrix-internal/identity/v1/check_credentials", method = RequestMethod.POST)
public String checkCredentials(HttpServletRequest req) {
try {
JsonElement el = new JsonParser().parse(IOUtils.toString(req.getInputStream(), StandardCharsets.UTF_8));
if (!el.isJsonObject() || !el.getAsJsonObject().has("user")) {
throw new IllegalArgumentException("Missing user key");
}
JsonObject authData = el.getAsJsonObject().get("user").getAsJsonObject();
if (!authData.has("id") || !authData.has("password")) {
throw new IllegalArgumentException("Missing id or password keys");
}
String id = authData.get("id").getAsString();
log.info("Requested to check credentials for {}", id);
String password = authData.get("password").getAsString();
UserAuthResult result = mgr.authenticate(id, password);
JsonObject authObj = new JsonObject();
authObj.addProperty("success", result.isSuccess());
if (result.isSuccess()) {
authObj.addProperty("mxid", result.getMxid());
authObj.addProperty("display_name", result.getDisplayName());
}
JsonObject obj = new JsonObject();
obj.add("authentication", authObj);
return gson.toJson(obj);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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;
import io.kamax.mxisd.lookup.ThreePidMapping;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
public class ClientBulkLookupAnswer {
private List<List<String>> threepids = new ArrayList<>();
public void addAll(Collection<ThreePidMapping> mappings) {
for (ThreePidMapping mapping : mappings) {
threepids.add(Arrays.asList(mapping.getMedium(), mapping.getValue(), mapping.getMxid()));
}
}
public List<List<String>> getThreepids() {
return threepids;
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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;
import io.kamax.mxisd.lookup.ThreePidMapping;
import java.util.ArrayList;
import java.util.List;
public class ClientBulkLookupRequest {
private List<List<String>> threepids = new ArrayList<>();
public List<List<String>> getThreepids() {
return threepids;
}
public void setThreepids(List<List<String>> threepids) {
this.threepids = threepids;
}
public void setMappings(List<ThreePidMapping> mappings) {
for (ThreePidMapping mapping : mappings) {
List<String> threepid = new ArrayList<>();
threepid.add(mapping.getMedium());
threepid.add(mapping.getValue());
threepids.add(threepid);
}
}
}

View File

@@ -0,0 +1,106 @@
/*
* 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;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import io.kamax.mxisd.exception.BadRequestException;
import io.kamax.mxisd.exception.InternalServerError;
import io.kamax.mxisd.exception.MappingAlreadyExistsException;
import io.kamax.mxisd.exception.MatrixException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.Instant;
@ControllerAdvice
@ResponseBody
@RequestMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class DefaultExceptionHandler {
private Logger log = LoggerFactory.getLogger(DefaultExceptionHandler.class);
private static Gson gson = new Gson();
static String handle(String erroCode, String error) {
JsonObject obj = new JsonObject();
obj.addProperty("errcode", erroCode);
obj.addProperty("error", error);
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)
@ExceptionHandler(MissingServletRequestParameterException.class)
public String handle(MissingServletRequestParameterException e) {
return handle("M_INVALID_BODY", e.getMessage());
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MappingAlreadyExistsException.class)
public String handle(MappingAlreadyExistsException e) {
return handle("M_ALREADY_EXISTS", e.getMessage());
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(BadRequestException.class)
public String handle(BadRequestException e) {
return handle("M_BAD_REQUEST", e.getMessage());
}
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(RuntimeException.class)
public String handle(HttpServletRequest req, RuntimeException e) {
log.error("Unknown error when handling {}", req.getRequestURL(), e);
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()
)
);
}
}

View File

@@ -0,0 +1,27 @@
/*
* 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;
public class IdentityAPIv1 {
public static final String BASE = "/_matrix/identity/api/v1";
}

View File

@@ -0,0 +1,82 @@
/*
* 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;
import com.google.gson.Gson;
import io.kamax.matrix.MatrixID;
import io.kamax.mxisd.config.ServerConfig;
import io.kamax.mxisd.controller.v1.io.ThreePidInviteReplyIO;
import io.kamax.mxisd.invitation.IThreePidInvite;
import io.kamax.mxisd.invitation.IThreePidInviteReply;
import io.kamax.mxisd.invitation.InvitationManager;
import io.kamax.mxisd.invitation.ThreePidInvite;
import io.kamax.mxisd.key.KeyManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
@RestController
@CrossOrigin
@RequestMapping(path = IdentityAPIv1.BASE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
class InvitationController {
private Logger log = LoggerFactory.getLogger(InvitationController.class);
@Autowired
private InvitationManager mgr;
@Autowired
private KeyManager keyMgr;
@Autowired
private ServerConfig srvCfg;
private Gson gson = new Gson();
@RequestMapping(value = "/store-invite", method = POST)
String store(
HttpServletRequest request,
@RequestParam String sender,
@RequestParam String medium,
@RequestParam String address,
@RequestParam("room_id") String roomId) {
Map<String, String> parameters = new HashMap<>();
for (String key : request.getParameterMap().keySet()) {
parameters.put(key, request.getParameter(key));
}
IThreePidInvite invite = new ThreePidInvite(new MatrixID(sender), medium, address, roomId, parameters);
IThreePidInviteReply reply = mgr.storeInvite(invite);
return gson.toJson(new ThreePidInviteReplyIO(reply, keyMgr.getPublicKeyBase64(keyMgr.getCurrentIndex()), srvCfg.getPublicUrl()));
}
}

View File

@@ -0,0 +1,81 @@
/*
* 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;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import io.kamax.mxisd.controller.v1.io.KeyValidityJson;
import io.kamax.mxisd.exception.BadRequestException;
import io.kamax.mxisd.key.KeyManager;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
@RestController
@CrossOrigin
@RequestMapping(path = IdentityAPIv1.BASE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class KeyController {
private Logger log = LoggerFactory.getLogger(KeyController.class);
@Autowired
private KeyManager keyMgr;
private Gson gson = new Gson();
private String validKey = gson.toJson(new KeyValidityJson(true));
private String invalidKey = gson.toJson(new KeyValidityJson(false));
@RequestMapping(value = "/pubkey/{keyType}:{keyId}", method = GET)
public String getKey(@PathVariable String keyType, @PathVariable int keyId) {
if (!"ed25519".contentEquals(keyType)) {
throw new BadRequestException("Invalid algorithm: " + keyType);
}
log.info("Key {}:{} was requested", keyType, keyId);
JsonObject obj = new JsonObject();
obj.addProperty("public_key", keyMgr.getPublicKeyBase64(keyId));
return gson.toJson(obj);
}
@RequestMapping(value = "/pubkey/ephemeral/isvalid", method = GET)
public String checkEphemeralKeyValidity(HttpServletRequest request) {
log.warn("Ephemeral key was request but no ephemeral key are generated, replying not valid");
return invalidKey;
}
@RequestMapping(value = "/pubkey/isvalid", method = GET)
public String checkKeyValidity(HttpServletRequest request, @RequestParam("public_key") String pubKey) {
log.info("Validating public key {}", pubKey);
// TODO do in manager
boolean valid = StringUtils.equals(pubKey, keyMgr.getPublicKeyBase64(keyMgr.getCurrentIndex()));
return valid ? validKey : invalidKey;
}
}

View File

@@ -0,0 +1,130 @@
/*
* 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;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import io.kamax.mxisd.controller.v1.io.SingeLookupReplyJson;
import io.kamax.mxisd.exception.InternalServerError;
import io.kamax.mxisd.lookup.*;
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
import io.kamax.mxisd.signature.SignatureManager;
import io.kamax.mxisd.util.GsonParser;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
@RestController
@CrossOrigin
@RequestMapping(path = IdentityAPIv1.BASE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class MappingController {
private Logger log = LoggerFactory.getLogger(MappingController.class);
private Gson gson = new Gson();
private GsonParser parser = new GsonParser(gson);
@Autowired
private LookupStrategy strategy;
@Autowired
private SignatureManager signMgr;
private void setRequesterInfo(ALookupRequest lookupReq, HttpServletRequest req) {
lookupReq.setRequester(req.getRemoteAddr());
String xff = req.getHeader("X-FORWARDED-FOR");
lookupReq.setRecursive(StringUtils.isNotBlank(xff));
if (lookupReq.isRecursive()) {
lookupReq.setRecurseHosts(Arrays.asList(xff.split(",")));
}
lookupReq.setUserAgent(req.getHeader("USER-AGENT"));
}
@RequestMapping(value = "/lookup", method = GET)
String lookup(HttpServletRequest request, @RequestParam String medium, @RequestParam String address) {
SingleLookupRequest lookupRequest = new SingleLookupRequest();
setRequesterInfo(lookupRequest, request);
lookupRequest.setType(medium);
lookupRequest.setThreePid(address);
log.info("Got single lookup request from {} with client {} - Is recursive? {}", lookupRequest.getRequester(), lookupRequest.getUserAgent(), lookupRequest.isRecursive());
Optional<SingleLookupReply> lookupOpt = strategy.find(lookupRequest);
if (!lookupOpt.isPresent()) {
log.info("No mapping was found, return empty JSON object");
return "{}";
}
SingleLookupReply lookup = lookupOpt.get();
if (lookup.isSigned()) {
log.info("Lookup is already signed, sending as-is");
return lookup.getBody();
} else {
log.info("Lookup is not signed, signing");
JsonObject obj = gson.toJsonTree(new SingeLookupReplyJson(lookup)).getAsJsonObject();
obj.add("signatures", signMgr.signMessageGson(gson.toJson(obj)));
return gson.toJson(obj);
}
}
@RequestMapping(value = "/bulk_lookup", method = POST)
String bulkLookup(HttpServletRequest request) {
BulkLookupRequest lookupRequest = new BulkLookupRequest();
setRequesterInfo(lookupRequest, request);
log.info("Got single lookup request from {} with client {} - Is recursive? {}", lookupRequest.getRequester(), lookupRequest.getUserAgent(), lookupRequest.isRecursive());
try {
ClientBulkLookupRequest input = parser.parse(request, ClientBulkLookupRequest.class);
List<ThreePidMapping> mappings = new ArrayList<>();
for (List<String> mappingRaw : input.getThreepids()) {
ThreePidMapping mapping = new ThreePidMapping();
mapping.setMedium(mappingRaw.get(0));
mapping.setValue(mappingRaw.get(1));
mappings.add(mapping);
}
lookupRequest.setMappings(mappings);
ClientBulkLookupAnswer answer = new ClientBulkLookupAnswer();
answer.addAll(strategy.find(lookupRequest));
return gson.toJson(answer);
} catch (IOException e) {
throw new InternalServerError(e);
}
}
}

View File

@@ -0,0 +1,91 @@
/*
* 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;
import io.kamax.mxisd.config.ServerConfig;
import io.kamax.mxisd.config.ViewConfig;
import io.kamax.mxisd.controller.v1.remote.RemoteIdentityAPIv1;
import io.kamax.mxisd.exception.InternalServerError;
import io.kamax.mxisd.session.SessionMananger;
import io.kamax.mxisd.session.ValidationResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Controller
@RequestMapping(path = IdentityAPIv1.BASE)
class SessionController {
private Logger log = LoggerFactory.getLogger(SessionController.class);
@Autowired
private ServerConfig srvCfg;
@Autowired
private SessionMananger mgr;
@Autowired
private ViewConfig viewCfg;
;
@RequestMapping(value = "/validate/{medium}/submitToken")
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(), request.getQueryString());
ValidationResult r = mgr.validate(sid, secret, token);
log.info("Session {} was validated", sid);
if (r.getNextUrl().isPresent()) {
String url = srvCfg.getPublicUrl() + r.getNextUrl().get();
log.info("Session {} validation: next URL is present, redirecting to {}", sid, url);
try {
response.sendRedirect(url);
return "";
} catch (IOException e) {
log.warn("Unable to redirect user to {}", url);
throw new InternalServerError(e);
}
} else {
if (r.isCanRemote()) {
String url = srvCfg.getPublicUrl() + RemoteIdentityAPIv1.getRequestToken(r.getSession().getId(), r.getSession().getSecret());
model.addAttribute("remoteSessionLink", url);
return viewCfg.getSession().getLocalRemote().getOnTokenSubmit().getSuccess();
} else {
return viewCfg.getSession().getLocal().getOnTokenSubmit().getSuccess();
}
}
}
}

View File

@@ -0,0 +1,159 @@
/*
* 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;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import io.kamax.matrix.ThreePidMedium;
import io.kamax.mxisd.ThreePid;
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.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.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.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@RestController
@CrossOrigin
@RequestMapping(path = IdentityAPIv1.BASE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class SessionRestController {
private Logger log = LoggerFactory.getLogger(SessionRestController.class);
private class Sid { // FIXME replace with RequestTokenResponse
private String sid;
public Sid(String sid) {
setSid(sid);
}
String getSid() {
return sid;
}
void setSid(String sid) {
this.sid = sid;
}
}
@Autowired
private ServerConfig srvCfg;
@Autowired
private SessionMananger mgr;
@Autowired
private InvitationManager invMgr;
@Autowired
private ViewConfig viewCfg;
private Gson gson = new Gson();
private GsonParser parser = new GsonParser(gson);
@RequestMapping(value = "/validate/{medium}/requestToken")
String init(HttpServletRequest request, HttpServletResponse response, @PathVariable String medium) throws IOException {
log.info("Request {}: {}", request.getMethod(), request.getRequestURL(), request.getQueryString());
if (ThreePidMedium.Email.is(medium)) {
SessionEmailTokenRequestJson req = parser.parse(request, SessionEmailTokenRequestJson.class);
return gson.toJson(new Sid(mgr.create(
request.getRemoteHost(),
new ThreePid(req.getMedium(), req.getValue()),
req.getSecret(),
req.getAttempt(),
req.getNextLink())));
}
if (ThreePidMedium.PhoneNumber.is(medium)) {
SessionPhoneTokenRequestJson req = parser.parse(request, SessionPhoneTokenRequestJson.class);
return gson.toJson(new Sid(mgr.create(
request.getRemoteHost(),
new ThreePid(req.getMedium(), req.getValue()),
req.getSecret(),
req.getAttempt(),
req.getNextLink())));
}
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 = "/3pid/getValidated3pid")
String check(HttpServletRequest request, HttpServletResponse response,
@RequestParam String sid, @RequestParam("client_secret") String secret) {
log.info("Requested: {}", request.getRequestURL(), request.getQueryString());
try {
ThreePidValidation pid = mgr.getValidated(sid, secret);
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);
} catch (SessionNotValidatedException e) {
log.info("Session {} was requested but has not yet been validated", sid);
throw e;
}
}
@RequestMapping(value = "/3pid/bind")
String bind(HttpServletRequest request, HttpServletResponse response,
@RequestParam String sid, @RequestParam("client_secret") String secret, @RequestParam String mxid) {
log.info("Requested: {}", request.getRequestURL(), request.getQueryString());
try {
mgr.bind(sid, secret, mxid);
return "{}";
} catch (BadRequestException e) {
log.info("requested session was not validated");
JsonObject 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);
} finally {
// If a user registers, there is no standard login event. Instead, this is the only way to trigger
// resolution at an appropriate time. Meh at synapse/Riot!
invMgr.lookupMappingsForInvites();
}
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@CrossOrigin
@RequestMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class StatusController {
private Gson gson = new Gson();
@RequestMapping(value = "/_matrix/identity/status")
public String getStatus() {
// TODO link to backend
JsonObject status = new JsonObject();
status.addProperty("health", "OK");
JsonObject obj = new JsonObject();
obj.add("status", status);
return gson.toJson(obj);
}
}

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.controller.v1.io;
public abstract class GenericTokenRequestJson {
private String client_secret;
private int send_attempt;
private String next_link;
public String getSecret() {
return client_secret;
}
public int getAttempt() {
return send_attempt;
}
public String getNextLink() {
return next_link;
}
}

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 KeyValidityJson {
private boolean valid;
public KeyValidityJson(boolean isValid) {
this.valid = isValid;
}
public boolean isValid() {
return valid;
}
}

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.controller.v1.io;
public class RequestTokenResponse {
private String sid;
public String getSid() {
return sid;
}
}

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 SessionEmailTokenRequestJson extends GenericTokenRequestJson {
private String email;
public String getMedium() {
return "email";
}
public String getValue() {
return email;
}
}

View File

@@ -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.controller.v1.io;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber;
public class SessionPhoneTokenRequestJson extends GenericTokenRequestJson {
private static PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
private String country;
private String phone_number;
public String getMedium() {
return "msisdn";
}
public String getValue() {
try {
Phonenumber.PhoneNumber num = phoneUtil.parse(phone_number, country);
return phoneUtil.format(num, PhoneNumberUtil.PhoneNumberFormat.E164).replace("+", "");
} catch (NumberParseException e) {
throw new IllegalArgumentException("Invalid phone number");
}
}
}

View File

@@ -0,0 +1,75 @@
/*
* 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;
import io.kamax.mxisd.lookup.SingleLookupReply;
import java.util.HashMap;
import java.util.Map;
public class SingeLookupReplyJson {
private String address;
private String medium;
private String mxid;
private long not_after;
private long not_before;
private long ts;
private Map<String, Map<String, String>> signatures = new HashMap<>();
public SingeLookupReplyJson(SingleLookupReply reply) {
this.address = reply.getRequest().getThreePid();
this.medium = reply.getRequest().getType();
this.mxid = reply.getMxid().getId();
this.not_after = reply.getNotAfter().toEpochMilli();
this.not_before = reply.getNotBefore().toEpochMilli();
this.ts = reply.getTimestamp().toEpochMilli();
}
public String getAddress() {
return address;
}
public String getMedium() {
return medium;
}
public String getMxid() {
return mxid;
}
public long getNot_after() {
return not_after;
}
public long getNot_before() {
return not_before;
}
public long getTs() {
return ts;
}
public boolean isSigned() {
return signatures != null && !signatures.isEmpty();
}
}

View File

@@ -0,0 +1,70 @@
/*
* 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;
import io.kamax.mxisd.invitation.IThreePidInviteReply;
import java.util.Collections;
import java.util.List;
public class ThreePidInviteReplyIO {
private String token;
private List<Key> public_keys;
private String display_name;
public ThreePidInviteReplyIO(IThreePidInviteReply reply, String pubKey, String publicUrl) {
this.token = reply.getToken();
this.public_keys = Collections.singletonList(new Key(pubKey, publicUrl));
this.display_name = reply.getDisplayName();
}
public String getToken() {
return token;
}
public List<Key> getPublic_keys() {
return public_keys;
}
public String getDisplay_name() {
return display_name;
}
public class Key {
private String key_validity_url;
private String public_key;
public Key(String key, String publicUrl) {
this.key_validity_url = publicUrl + "/_matrix/identity/api/v1/pubkey/isvalid";
this.public_key = key;
}
public String getKey_validity_url() {
return key_validity_url;
}
public String getPublic_key() {
return public_key;
}
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.remote;
public class RemoteIdentityAPIv1 {
public static final String BASE = "/_matrix/identity/remote/api/v1";
public static final String SESSION_REQUEST_TOKEN = BASE + "/validate/requestToken";
public static final String SESSION_CHECK = BASE + "/validate/check";
public static String getRequestToken(String id, String secret) {
return SESSION_REQUEST_TOKEN + "?sid=" + id + "&client_secret=" + secret;
}
public static String getSessionCheck(String id, String secret) {
return SESSION_CHECK + "?sid=" + id + "&client_secret=" + secret;
}
}

View File

@@ -0,0 +1,59 @@
package io.kamax.mxisd.controller.v1.remote;
import io.kamax.mxisd.config.ViewConfig;
import io.kamax.mxisd.exception.SessionNotValidatedException;
import io.kamax.mxisd.session.SessionMananger;
import io.kamax.mxisd.threepid.session.IThreePidSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
import static io.kamax.mxisd.controller.v1.remote.RemoteIdentityAPIv1.SESSION_CHECK;
import static io.kamax.mxisd.controller.v1.remote.RemoteIdentityAPIv1.SESSION_REQUEST_TOKEN;
@Controller
public class RemoteSessionController {
private Logger log = LoggerFactory.getLogger(RemoteSessionController.class);
@Autowired
private ViewConfig viewCfg;
@Autowired
private SessionMananger mgr;
@RequestMapping(path = SESSION_REQUEST_TOKEN)
public String requestToken(
HttpServletRequest request,
@RequestParam String sid,
@RequestParam("client_secret") String secret,
Model model
) {
log.info("Request {}: {}", request.getMethod(), request.getRequestURL());
IThreePidSession session = mgr.createRemote(sid, secret);
model.addAttribute("checkLink", RemoteIdentityAPIv1.getSessionCheck(session.getId(), session.getSecret()));
return viewCfg.getSession().getRemote().getOnRequest().getSuccess();
}
@RequestMapping(path = SESSION_CHECK)
public String check(
HttpServletRequest request,
@RequestParam String sid,
@RequestParam("client_secret") String secret) {
log.info("Request {}: {}", request.getMethod(), request.getRequestURL());
try {
mgr.validateRemote(sid, secret);
return viewCfg.getSession().getRemote().getOnCheck().getSuccess();
} catch (SessionNotValidatedException e) {
return viewCfg.getSession().getRemote().getOnCheck().getFailure();
}
}
}