Start structural port from Spring Boot to Undertow

This commit is contained in:
Max Dor
2018-12-25 05:32:00 +01:00
parent df44428a85
commit 05493da27c
195 changed files with 3313 additions and 2079 deletions

View File

@@ -0,0 +1,129 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.exception.HttpMatrixException;
import io.kamax.mxisd.exception.InternalServerError;
import io.kamax.mxisd.proxy.Response;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.HttpString;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
public abstract class BasicHttpHandler implements HttpHandler {
private transient final Logger log = LoggerFactory.getLogger(BasicHttpHandler.class);
protected String getRemoteHostAddress(HttpServerExchange exchange) {
return ((InetSocketAddress) exchange.getConnection().getPeerAddress()).getAddress().getHostAddress();
}
protected String getQueryParameter(HttpServerExchange exchange, String name) {
try {
String raw = exchange.getQueryParameters().getOrDefault(name, new LinkedList<>()).peekFirst();
return URLDecoder.decode(raw, StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException e) {
throw new InternalServerError(e);
}
}
protected String getPathVariable(HttpServerExchange exchange, String name) {
return getQueryParameter(exchange, name);
}
protected void writeBodyAsUtf8(HttpServerExchange exchange, String body) {
exchange.getResponseSender().send(body, StandardCharsets.UTF_8);
}
protected <T> T parseJsonTo(HttpServerExchange exchange, Class<T> type) {
try {
return GsonUtil.get().fromJson(IOUtils.toString(exchange.getInputStream(), StandardCharsets.UTF_8), type);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
protected JsonObject parseJsonObject(HttpServerExchange exchange, String key) {
try {
JsonObject base = GsonUtil.parseObj(IOUtils.toString(exchange.getInputStream(), StandardCharsets.UTF_8));
return GsonUtil.getObj(base, key);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
protected void respond(HttpServerExchange ex, int statusCode, JsonElement bodyJson) {
respondJson(ex, statusCode, GsonUtil.get().toJson(bodyJson));
}
protected void respond(HttpServerExchange ex, JsonElement bodyJson) {
respond(ex, 200, bodyJson);
}
protected void respondJson(HttpServerExchange ex, int status, String body) {
ex.setStatusCode(status);
ex.getResponseHeaders().put(HttpString.tryFromString("Content-Type"), "application/json");
writeBodyAsUtf8(ex, body);
}
protected void respondJson(HttpServerExchange ex, String body) {
respondJson(ex, 200, body);
}
protected void respondJson(HttpServerExchange ex, Object body) {
respondJson(ex, GsonUtil.get().toJson(body));
}
protected JsonObject buildErrorBody(HttpServerExchange exchange, String errCode, String error) {
JsonObject obj = new JsonObject();
obj.addProperty("errcode", errCode);
obj.addProperty("error", error);
obj.addProperty("success", false);
log.info("Request {} {} - Error {}: {}", exchange.getRequestMethod(), exchange.getRequestURL(), errCode, error);
return obj;
}
protected void respond(HttpServerExchange ex, int status, String errCode, String error) {
respond(ex, status, buildErrorBody(ex, errCode, error));
}
protected void handleException(HttpServerExchange exchange, HttpMatrixException ex) {
respond(exchange, ex.getStatus(), buildErrorBody(exchange, ex.getErrorCode(), ex.getError()));
}
protected void respond(HttpServerExchange exchange, Response upstream) {
exchange.setStatusCode(upstream.getStatus());
upstream.getHeaders().forEach((key, value) -> exchange.getResponseHeaders().addAll(HttpString.tryFromString(key), value));
writeBodyAsUtf8(exchange, upstream.getBody());
}
}

View File

@@ -0,0 +1,55 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler;
import io.kamax.mxisd.exception.AccessTokenNotFoundException;
import io.kamax.mxisd.util.OptionalUtil;
import io.undertow.server.HttpServerExchange;
import org.apache.commons.lang3.StringUtils;
import java.util.LinkedList;
import java.util.Optional;
public abstract class HomeserverProxyHandler extends BasicHttpHandler {
protected final static String headerName = "Authorization";
protected final static String headerValuePrefix = "Bearer ";
private final static String parameterName = "access_token";
Optional<String> findAccessTokenInHeaders(HttpServerExchange exchange) {
return Optional.ofNullable(exchange.getRequestHeaders().getFirst(headerName))
.filter(header -> StringUtils.startsWith(header, headerValuePrefix))
.map(header -> header.substring(headerValuePrefix.length()));
}
Optional<String> findAccessTokenInQuery(HttpServerExchange exchange) {
return Optional.ofNullable(exchange.getQueryParameters().getOrDefault(parameterName, new LinkedList<>()).peekFirst());
}
public Optional<String> findAccessToken(HttpServerExchange exchange) {
return OptionalUtil.findFirst(() -> findAccessTokenInHeaders(exchange), () -> findAccessTokenInQuery(exchange));
}
public String getAccessToken(HttpServerExchange exchange) {
return findAccessToken(exchange).orElseThrow(AccessTokenNotFoundException::new);
}
}

View File

@@ -0,0 +1,112 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler;
import com.google.gson.JsonSyntaxException;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.exception.*;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.Instant;
public class SaneHandler extends BasicHttpHandler {
public static SaneHandler around(HttpHandler h) {
return new SaneHandler(h);
}
private transient final Logger log = LoggerFactory.getLogger(SaneHandler.class);
private HttpHandler child;
public SaneHandler(HttpHandler child) {
this.child = child;
}
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
exchange.startBlocking();
if (exchange.isInIoThread()) {
exchange.dispatch(this);
} else {
try {
child.handleRequest(exchange);
} catch (IllegalArgumentException e) {
respond(exchange, HttpStatus.SC_BAD_REQUEST, GsonUtil.makeObj("error", e.getMessage()));
} catch (BadRequestException e) {
respond(exchange, HttpStatus.SC_BAD_REQUEST, "M_BAD_REQUEST", e.getMessage());
} catch (MappingAlreadyExistsException e) {
respond(exchange, HttpStatus.SC_BAD_REQUEST, "M_ALREADY_EXISTS", e.getMessage());
} catch (JsonMemberNotFoundException e) {
respond(exchange, HttpStatus.SC_BAD_REQUEST, "M_JSON_MISSING_KEYS", e.getMessage());
} catch (InvalidResponseJsonException | JsonSyntaxException e) {
respond(exchange, HttpStatus.SC_BAD_REQUEST, "M_INVALID_JSON", e.getMessage());
} catch (InvalidCredentialsException e) {
respond(exchange, HttpStatus.SC_UNAUTHORIZED, "M_UNAUTHORIZED", e.getMessage());
} catch (ObjectNotFoundException e) {
respond(exchange, HttpStatus.SC_NOT_FOUND, "M_NOT_FOUND", e.getMessage());
} catch (NotImplementedException e) {
respond(exchange, HttpStatus.SC_NOT_IMPLEMENTED, "M_NOT_IMPLEMENTED", e.getMessage());
} catch (FeatureNotAvailable e) {
if (StringUtils.isNotBlank(e.getInternalReason())) {
log.error("Feature not available: {}", e.getInternalReason());
}
handleException(exchange, e);
} catch (InternalServerError e) {
if (StringUtils.isNotBlank(e.getInternalReason())) {
log.error("Reference #{} - {}", e.getReference(), e.getInternalReason());
} else {
log.error("Reference #{}", e);
}
handleException(exchange, e);
} catch (RemoteLoginException e) {
if (e.getErrorBodyMsgResp() != null) {
respond(exchange, e.getStatus(), e.getErrorBodyMsgResp());
} else {
handleException(exchange, e);
}
} catch (HttpMatrixException e) {
respond(exchange, e.getStatus(), buildErrorBody(exchange, e.getErrorCode(), e.getError()));
} catch (RuntimeException e) {
log.error("Unknown error when handling {}", exchange.getRequestURL(), e);
respond(exchange, HttpStatus.SC_INTERNAL_SERVER_ERROR, buildErrorBody(exchange,
"M_UNKNOWN",
StringUtils.defaultIfBlank(
e.getMessage(),
"An internal server error occurred. If this error persists, please contact support with reference #" +
Instant.now().toEpochMilli()
)
));
} finally {
exchange.endExchange();
}
}
}
}

View File

@@ -0,0 +1,38 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.as.v1;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.undertow.server.HttpServerExchange;
import java.util.LinkedList;
import java.util.Optional;
public abstract class ApplicationServiceHandler extends BasicHttpHandler {
protected String getToken(HttpServerExchange ex) {
return Optional.ofNullable(ex.getQueryParameters()
.getOrDefault("access_token", new LinkedList<>())
.peekFirst()
).orElse("");
}
}

View File

@@ -0,0 +1,42 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.as.v1;
import io.kamax.mxisd.as.AppSvcManager;
import io.kamax.mxisd.exception.NotFoundException;
import io.undertow.server.HttpServerExchange;
public class AsNotFoundHandler extends ApplicationServiceHandler {
private final AppSvcManager app;
public AsNotFoundHandler(AppSvcManager app) {
this.app = app;
}
@Override
public void handleRequest(HttpServerExchange exchange) {
app.withToken(getToken(exchange));
throw new NotFoundException();
}
}

View File

@@ -0,0 +1,46 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.as.v1;
import io.kamax.mxisd.as.AppSvcManager;
import io.undertow.server.HttpServerExchange;
import java.util.LinkedList;
public class AsTransactionHandler extends ApplicationServiceHandler {
public static final String ID = "txnId";
public static final String Path = "/_matrix/app/v1/transactions/{" + ID + "}";
private final AppSvcManager app;
public AsTransactionHandler(AppSvcManager app) {
this.app = app;
}
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
String txnId = exchange.getQueryParameters().getOrDefault(ID, new LinkedList<>()).peekFirst();
respondJson(exchange, app.withToken(getToken(exchange))
.processTransaction(txnId, exchange.getInputStream()).get());
}
}

View File

@@ -0,0 +1,70 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.auth;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.auth.AuthManager;
import io.kamax.mxisd.auth.UserAuthResult;
import io.kamax.mxisd.exception.JsonMemberNotFoundException;
import io.kamax.mxisd.http.io.CredentialsValidationResponse;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.undertow.server.HttpServerExchange;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RestAuthHandler extends BasicHttpHandler {
public static final String Path = "/_matrix-internal/identity/v1/check_credentials";
private transient final Logger log = LoggerFactory.getLogger(RestAuthHandler.class);
private AuthManager mgr;
public RestAuthHandler(AuthManager mgr) {
this.mgr = mgr;
}
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
JsonObject authData = parseJsonObject(exchange, "user");
if (!authData.has("id") || !authData.has("password")) {
throw new JsonMemberNotFoundException("Missing id or password keys");
}
String id = GsonUtil.getStringOrThrow(authData, "id");
log.info("Requested to check credentials for {}", id);
String password = GsonUtil.getStringOrThrow(authData, "password");
UserAuthResult result = mgr.authenticate(id, password);
CredentialsValidationResponse response = new CredentialsValidationResponse(result.isSuccess());
if (result.isSuccess()) {
response.setDisplayName(result.getDisplayName());
response.getProfile().setThreePids(result.getThreePids());
}
JsonElement authObj = GsonUtil.get().toJsonTree(response);
respond(exchange, GsonUtil.makeObj("auth", authObj));
}
}

View File

@@ -0,0 +1,52 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.auth.v1;
import io.kamax.mxisd.auth.AuthManager;
import io.undertow.server.HttpServerExchange;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import java.net.URI;
public class LoginGetHandler extends LoginHandler {
private AuthManager mgr;
private CloseableHttpClient client;
public LoginGetHandler(AuthManager mgr, CloseableHttpClient client) {
this.mgr = mgr;
this.client = client;
}
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
URI target = URI.create(exchange.getRequestURL());
try (CloseableHttpResponse hsResponse = client.execute(new HttpGet(mgr.resolveProxyUrl(target)))) {
// TODO deal with headers, content-type, encoding, etc.
respondJson(exchange, hsResponse.getStatusLine().getStatusCode(), EntityUtils.toString(hsResponse.getEntity()));
}
}
}

View File

@@ -0,0 +1,29 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.auth.v1;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
public abstract class LoginHandler extends BasicHttpHandler {
public static final String Path = "/_matrix/client/r0/login";
}

View File

@@ -0,0 +1,47 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.auth.v1;
import io.kamax.mxisd.auth.AuthManager;
import io.undertow.server.HttpServerExchange;
import org.apache.commons.io.IOUtils;
import java.net.URI;
import java.nio.charset.StandardCharsets;
public class LoginPostHandler extends LoginHandler {
private AuthManager mgr;
public LoginPostHandler(AuthManager mgr) {
this.mgr = mgr;
}
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
respondJson(exchange, mgr.proxyLogin(
URI.create(exchange.getRequestURL()),
IOUtils.toString(exchange.getInputStream(), StandardCharsets.UTF_8)
)
);
}
}

View File

@@ -0,0 +1,52 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.directory.v1;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.directory.DirectoryManager;
import io.kamax.mxisd.http.io.UserDirectorySearchRequest;
import io.kamax.mxisd.http.io.UserDirectorySearchResult;
import io.kamax.mxisd.http.undertow.handler.HomeserverProxyHandler;
import io.undertow.server.HttpServerExchange;
import java.net.URI;
public class UserDirectorySearchHandler extends HomeserverProxyHandler {
public static final String Path = "/_matrix/client/r0/user_directory/search";
private DirectoryManager mgr;
public UserDirectorySearchHandler(DirectoryManager mgr) {
this.mgr = mgr;
}
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
String accessToken = getAccessToken(exchange);
UserDirectorySearchRequest searchQuery = parseJsonTo(exchange, UserDirectorySearchRequest.class);
URI target = URI.create(exchange.getRequestURL());
UserDirectorySearchResult result = mgr.search(target, accessToken, searchQuery.getSearchTerm());
respondJson(exchange, GsonUtil.get().toJson(result));
}
}

View File

@@ -0,0 +1,70 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.identity.v1;
import io.kamax.mxisd.http.IsAPIv1;
import io.kamax.mxisd.http.io.identity.ClientBulkLookupAnswer;
import io.kamax.mxisd.http.io.identity.ClientBulkLookupRequest;
import io.kamax.mxisd.lookup.BulkLookupRequest;
import io.kamax.mxisd.lookup.ThreePidMapping;
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
import io.undertow.server.HttpServerExchange;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
public class BulkLookupHandler extends LookupHandler {
public static final String Path = IsAPIv1.Base + "/bulk_lookup";
private transient final Logger log = LoggerFactory.getLogger(SingleLookupHandler.class);
private LookupStrategy strategy;
public BulkLookupHandler(LookupStrategy strategy) {
this.strategy = strategy;
}
@Override
public void handleRequest(HttpServerExchange exchange) {
ClientBulkLookupRequest input = parseJsonTo(exchange, ClientBulkLookupRequest.class);
BulkLookupRequest lookupRequest = new BulkLookupRequest();
setRequesterInfo(lookupRequest, exchange);
log.info("Got bulk lookup request from {} with client {} - Is recursive? {}",
lookupRequest.getRequester(), lookupRequest.getUserAgent(), lookupRequest.isRecursive());
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));
respondJson(exchange, answer);
}
}

View File

@@ -0,0 +1,41 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.identity.v1;
import io.kamax.mxisd.http.IsAPIv1;
import io.undertow.server.HttpServerExchange;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EphemeralKeyIsValidHandler extends KeyIsValidHandler {
public static final String Path = IsAPIv1.Base + "/pubkey/ephemeral/isvalid";
private transient final Logger log = LoggerFactory.getLogger(EphemeralKeyIsValidHandler.class);
@Override
public void handleRequest(HttpServerExchange exchange) {
log.warn("Ephemeral key was requested but no ephemeral key are generated, replying not valid");
respondJson(exchange, invalidKey);
}
}

View File

@@ -0,0 +1,36 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.identity.v1;
import io.kamax.mxisd.http.IsAPIv1;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.undertow.server.HttpServerExchange;
public class HelloHandler extends BasicHttpHandler {
public static final String Path = IsAPIv1.Base;
@Override
public void handleRequest(HttpServerExchange exchange) {
respondJson(exchange, "{}");
}
}

View File

@@ -0,0 +1,62 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.identity.v1;
import com.google.gson.JsonObject;
import io.kamax.matrix.crypto.KeyManager;
import io.kamax.mxisd.exception.BadRequestException;
import io.kamax.mxisd.http.IsAPIv1;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.undertow.server.HttpServerExchange;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class KeyGetHandler extends BasicHttpHandler {
public static final String Key = "key";
public static final String Path = IsAPIv1.Base + "/pubkey/{key}";
private transient final Logger log = LoggerFactory.getLogger(KeyGetHandler.class);
private KeyManager mgr;
public KeyGetHandler(KeyManager mgr) {
this.mgr = mgr;
}
@Override
public void handleRequest(HttpServerExchange exchange) {
String key = getQueryParameter(exchange, Key);
String[] v = key.split(":", 1);
String keyType = v[0];
int keyId = Integer.parseInt(v[1]);
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", mgr.getPublicKeyBase64(keyId));
respond(exchange, obj);
}
}

View File

@@ -0,0 +1,32 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.identity.v1;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.http.io.identity.KeyValidityJson;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
public abstract class KeyIsValidHandler extends BasicHttpHandler {
protected final String validKey = GsonUtil.get().toJson(new KeyValidityJson(true));
protected final String invalidKey = GsonUtil.get().toJson(new KeyValidityJson(false));
}

View File

@@ -0,0 +1,51 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.identity.v1;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.kamax.mxisd.lookup.ALookupRequest;
import io.undertow.server.HttpServerExchange;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetSocketAddress;
import java.util.Arrays;
public abstract class LookupHandler extends BasicHttpHandler {
private transient final Logger log = LoggerFactory.getLogger(LookupHandler.class);
protected void setRequesterInfo(ALookupRequest lookup, HttpServerExchange exchange) {
InetSocketAddress addr = (InetSocketAddress) exchange.getConnection().getPeerAddress();
lookup.setRequester(addr.getAddress().getHostAddress());
String xff = exchange.getRequestHeaders().getFirst("X-Forwarded-For");
log.debug("XFF header: {}", xff);
lookup.setRecursive(StringUtils.isBlank(xff));
if (!lookup.isRecursive()) {
lookup.setRecurseHosts(Arrays.asList(xff.split(",")));
lookup.setRequester(lookup.getRecurseHosts().get(lookup.getRecurseHosts().size() - 1));
}
lookup.setUserAgent(exchange.getRequestHeaders().getFirst("User-Agent"));
}
}

View File

@@ -0,0 +1,52 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.identity.v1;
import io.kamax.matrix.crypto.KeyManager;
import io.kamax.mxisd.http.IsAPIv1;
import io.undertow.server.HttpServerExchange;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RegularKeyIsValidHandler extends KeyIsValidHandler {
public static final String Path = IsAPIv1.Base + "/pubkey/isvalid";
private transient final Logger log = LoggerFactory.getLogger(RegularKeyIsValidHandler.class);
private KeyManager mgr;
public RegularKeyIsValidHandler(KeyManager mgr) {
this.mgr = mgr;
}
@Override
public void handleRequest(HttpServerExchange exchange) {
String pubKey = getQueryParameter(exchange, "public_key");
log.info("Validating public key {}", pubKey);
// TODO do in manager
boolean valid = StringUtils.equals(pubKey, mgr.getPublicKeyBase64(mgr.getCurrentIndex()));
respondJson(exchange, valid ? validKey : invalidKey);
}
}

View File

@@ -0,0 +1,37 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2017 Kamax Sarl
*
* https://www.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.http.undertow.handler.identity.v1;
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,62 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.identity.v1;
import io.kamax.mxisd.config.ViewConfig;
import io.kamax.mxisd.exception.SessionNotValidatedException;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.kamax.mxisd.session.SessionMananger;
import io.undertow.server.HttpServerExchange;
import org.apache.commons.io.IOUtils;
import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;
public class RemoteSessionCheckHandler extends BasicHttpHandler {
private SessionMananger mgr;
private ViewConfig viewCfg;
public RemoteSessionCheckHandler(SessionMananger mgr, ViewConfig viewCfg) {
this.mgr = mgr;
this.viewCfg = viewCfg;
}
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
String sid = getQueryParameter(exchange, "sid");
String secret = getQueryParameter(exchange, "client_secret");
try {
FileInputStream f = new FileInputStream(viewCfg.getSession().getRemote().getOnCheck().getSuccess());
String viewData = IOUtils.toString(f, StandardCharsets.UTF_8);
mgr.validateRemote(sid, secret);
writeBodyAsUtf8(exchange, viewData);
} catch (SessionNotValidatedException e) {
FileInputStream f = new FileInputStream(viewCfg.getSession().getRemote().getOnCheck().getFailure());
String viewData = IOUtils.toString(f, StandardCharsets.UTF_8);
writeBodyAsUtf8(exchange, viewData);
}
}
}

View File

@@ -0,0 +1,55 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.identity.v1;
import io.kamax.mxisd.config.ViewConfig;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.kamax.mxisd.session.SessionMananger;
import io.kamax.mxisd.threepid.session.IThreePidSession;
import io.undertow.server.HttpServerExchange;
import org.apache.commons.io.IOUtils;
import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;
public class RemoteSessionStartHandler extends BasicHttpHandler {
private SessionMananger mgr;
private ViewConfig viewCfg;
public RemoteSessionStartHandler(SessionMananger mgr, ViewConfig viewCfg) {
this.mgr = mgr;
this.viewCfg = viewCfg;
}
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
String sid = getQueryParameter(exchange, "sid");
String secret = getQueryParameter(exchange, "client_secret");
IThreePidSession session = mgr.createRemote(sid, secret);
FileInputStream f = new FileInputStream(viewCfg.getSession().getRemote().getOnRequest().getSuccess());
String rawData = IOUtils.toString(f, StandardCharsets.UTF_8);
String data = rawData.replace("${checkLink}", RemoteIdentityAPIv1.getSessionCheck(session.getId(), session.getSecret()));
writeBodyAsUtf8(exchange, data);
}
}

View File

@@ -0,0 +1,87 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.identity.v1;
import com.google.gson.JsonObject;
import io.kamax.matrix.ThreePid;
import io.kamax.matrix.ThreePidMedium;
import io.kamax.mxisd.http.IsAPIv1;
import io.kamax.mxisd.http.io.identity.RequestTokenResponse;
import io.kamax.mxisd.http.io.identity.SessionEmailTokenRequestJson;
import io.kamax.mxisd.http.io.identity.SessionPhoneTokenRequestJson;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.kamax.mxisd.session.SessionMananger;
import io.undertow.server.HttpServerExchange;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SessionStartHandler extends BasicHttpHandler {
public static final String Medium = "medium";
public static final String Path = IsAPIv1.Base + "/validate/{" + Medium + "}/requestToken";
private transient final Logger log = LoggerFactory.getLogger(SessionStartHandler.class);
private SessionMananger mgr;
public SessionStartHandler(SessionMananger mgr) {
this.mgr = mgr;
}
@Override
public void handleRequest(HttpServerExchange exchange) {
String medium = getPathVariable(exchange, "medium");
if (ThreePidMedium.Email.is(medium)) {
SessionEmailTokenRequestJson req = parseJsonTo(exchange, SessionEmailTokenRequestJson.class);
ThreePid threepid = new ThreePid(req.getMedium(), req.getValue());
respondJson(exchange, new RequestTokenResponse(mgr.create(
getRemoteHostAddress(exchange),
threepid,
req.getSecret(),
req.getAttempt(),
req.getNextLink())));
} else if (ThreePidMedium.PhoneNumber.is(medium)) {
SessionPhoneTokenRequestJson req = parseJsonTo(exchange, SessionPhoneTokenRequestJson.class);
ThreePid threepid = new ThreePid(req.getMedium(), req.getValue());
String sessionId = mgr.create(
getRemoteHostAddress(exchange),
threepid,
req.getSecret(),
req.getAttempt(),
req.getNextLink());
JsonObject res = new JsonObject();
res.addProperty("sid", sessionId);
res.addProperty(threepid.getMedium(), threepid.getAddress());
respond(exchange, res);
} else {
JsonObject obj = new JsonObject();
obj.addProperty("errcode", "M_INVALID_3PID_TYPE");
obj.addProperty("error", medium + " is not supported as a 3PID type");
respond(exchange, HttpStatus.SC_BAD_REQUEST, obj);
}
}
}

View File

@@ -0,0 +1,71 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.identity.v1;
import com.google.gson.JsonObject;
import io.kamax.mxisd.exception.BadRequestException;
import io.kamax.mxisd.http.IsAPIv1;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.kamax.mxisd.invitation.InvitationManager;
import io.kamax.mxisd.session.SessionMananger;
import io.undertow.server.HttpServerExchange;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SessionTpidBindHandler extends BasicHttpHandler {
public static final String Path = IsAPIv1.Base + "/3pid/bind";
private transient final Logger log = LoggerFactory.getLogger(SessionTpidBindHandler.class);
private SessionMananger mgr;
private InvitationManager invMgr;
public SessionTpidBindHandler(SessionMananger mgr, InvitationManager invMgr) {
this.mgr = mgr;
this.invMgr = invMgr;
}
@Override
public void handleRequest(HttpServerExchange exchange) {
String sid = getQueryParameter(exchange, "sid");
String secret = getQueryParameter(exchange, "client_secret");
String mxid = getQueryParameter(exchange, "mxid");
try {
mgr.bind(sid, secret, mxid);
respond(exchange, new JsonObject());
} 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());
respond(exchange, HttpStatus.SC_BAD_REQUEST, 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,65 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.identity.v1;
import com.google.gson.JsonObject;
import io.kamax.mxisd.exception.SessionNotValidatedException;
import io.kamax.mxisd.http.IsAPIv1;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.kamax.mxisd.lookup.ThreePidValidation;
import io.kamax.mxisd.session.SessionMananger;
import io.undertow.server.HttpServerExchange;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SessionTpidGetValidatedHandler extends BasicHttpHandler {
public static final String Path = IsAPIv1.Base + "/3pid/getValidated3pid";
private transient final Logger log = LoggerFactory.getLogger(SessionTpidGetValidatedHandler.class);
private SessionMananger mgr;
public SessionTpidGetValidatedHandler(SessionMananger mgr) {
this.mgr = mgr;
}
@Override
public void handleRequest(HttpServerExchange exchange) {
String sid = getQueryParameter(exchange, "sid");
String secret = getQueryParameter(exchange, "client_secret");
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());
respond(exchange, obj);
} catch (SessionNotValidatedException e) {
log.info("Session {} was requested but has not yet been validated", sid);
throw e;
}
}
}

View File

@@ -0,0 +1,124 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.identity.v1;
import io.kamax.mxisd.config.ServerConfig;
import io.kamax.mxisd.config.ViewConfig;
import io.kamax.mxisd.http.IsAPIv1;
import io.kamax.mxisd.http.io.identity.SuccessStatusJson;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.kamax.mxisd.session.SessionMananger;
import io.kamax.mxisd.session.ValidationResult;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.HttpString;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
public class SessionValidateHandler extends BasicHttpHandler {
public static final String Path = IsAPIv1.Base + "/validate/{medium}/submitToken";
private transient final Logger log = LoggerFactory.getLogger(SessionValidateHandler.class);
private SessionMananger mgr;
private ServerConfig srvCfg;
private ViewConfig viewCfg;
public SessionValidateHandler(SessionMananger mgr, ServerConfig srvCfg, ViewConfig viewCfg) {
this.mgr = mgr;
this.srvCfg = srvCfg;
this.viewCfg = viewCfg;
}
@Override
public void handleRequest(HttpServerExchange exchange) {
String medium = getQueryParameter(exchange, "medium");
String sid = getQueryParameter(exchange, "sid");
String secret = getQueryParameter(exchange, "client_secret");
String token = getQueryParameter(exchange, "token");
boolean isHtmlRequest = false;
for (String v : exchange.getRequestHeaders().get("Accept")) {
if (StringUtils.startsWithIgnoreCase(v, "text/html")) {
isHtmlRequest = true;
break;
}
}
if (isHtmlRequest) {
handleHtmlRequest(exchange, medium, sid, secret, token);
} else {
handleJsonRequest(exchange, medium, sid, secret, token);
}
}
public void handleHtmlRequest(HttpServerExchange exchange, String medium, String sid, String secret, String token) {
log.info("Validating session {} for medium {}", sid, medium);
ValidationResult r = mgr.validate(sid, secret, token);
log.info("Session {} was validated", sid);
if (r.getNextUrl().isPresent()) {
String url = r.getNextUrl().get();
try {
url = new URL(url).toString();
} catch (MalformedURLException e) {
log.info("Session next URL {} is not a valid one, will prepend public URL {}", url, srvCfg.getPublicUrl());
url = srvCfg.getPublicUrl() + r.getNextUrl().get();
}
log.info("Session {} validation: next URL is present, redirecting to {}", sid, url);
exchange.setStatusCode(302);
exchange.getResponseHeaders().add(HttpString.tryFromString("Location"), url);
} else {
try {
if (r.isCanRemote()) {
FileInputStream f = new FileInputStream(viewCfg.getSession().getLocalRemote().getOnTokenSubmit().getSuccess());
String url = srvCfg.getPublicUrl() + RemoteIdentityAPIv1.getRequestToken(r.getSession().getId(), r.getSession().getSecret());
String rawData = IOUtils.toString(f, StandardCharsets.UTF_8);
String data = rawData.replace("${remoteSessionLink}", url);
writeBodyAsUtf8(exchange, data);
} else {
FileInputStream f = new FileInputStream(viewCfg.getSession().getLocalRemote().getOnTokenSubmit().getSuccess());
String data = IOUtils.toString(f, StandardCharsets.UTF_8);
writeBodyAsUtf8(exchange, data);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public void handleJsonRequest(HttpServerExchange exchange, String medium, String sid, String secret, String token) {
log.info("Requested: {}", exchange.getRequestURL());
ValidationResult r = mgr.validate(sid, secret, token);
log.info("Session {} was validated", sid);
respondJson(exchange, new SuccessStatusJson(true));
}
}

View File

@@ -0,0 +1,81 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.identity.v1;
import com.google.gson.JsonObject;
import io.kamax.matrix.crypto.SignatureManager;
import io.kamax.matrix.event.EventKey;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.matrix.json.MatrixJson;
import io.kamax.mxisd.http.IsAPIv1;
import io.kamax.mxisd.http.io.identity.SingeLookupReplyJson;
import io.kamax.mxisd.lookup.SingleLookupReply;
import io.kamax.mxisd.lookup.SingleLookupRequest;
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
import io.undertow.server.HttpServerExchange;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Optional;
public class SingleLookupHandler extends LookupHandler {
public static final String Path = IsAPIv1.Base + "/lookup";
private transient final Logger log = LoggerFactory.getLogger(SingleLookupHandler.class);
private LookupStrategy strategy;
private SignatureManager signMgr;
public SingleLookupHandler(LookupStrategy strategy, SignatureManager signMgr) {
this.strategy = strategy;
this.signMgr = signMgr;
}
@Override
public void handleRequest(HttpServerExchange exchange) {
String medium = getQueryParameter(exchange, "medium");
String address = getQueryParameter(exchange, "address");
SingleLookupRequest lookupRequest = new SingleLookupRequest();
setRequesterInfo(lookupRequest, exchange);
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");
respondJson(exchange, "{}");
} else {
SingleLookupReply lookup = lookupOpt.get();
// FIXME signing should be done in the business model, not in the controller
JsonObject obj = GsonUtil.makeObj(new SingeLookupReplyJson(lookup));
obj.add(EventKey.Signatures.get(), signMgr.signMessageGson(MatrixJson.encodeCanonical(obj)));
respondJson(exchange, obj);
}
}
}

View File

@@ -0,0 +1,76 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.identity.v1;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix.crypto.KeyManager;
import io.kamax.mxisd.config.ServerConfig;
import io.kamax.mxisd.http.IsAPIv1;
import io.kamax.mxisd.http.io.identity.ThreePidInviteReplyIO;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
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.undertow.server.HttpServerExchange;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class StoreInviteHandler extends BasicHttpHandler {
public static final String Path = IsAPIv1.Base + "/store-invite";
private ServerConfig cfg;
private InvitationManager invMgr;
private KeyManager keyMgr;
public StoreInviteHandler(ServerConfig cfg, InvitationManager invMgr, KeyManager keyMgr) {
this.cfg = cfg;
this.invMgr = invMgr;
this.keyMgr = keyMgr;
}
@Override
public void handleRequest(HttpServerExchange exchange) {
Map<String, String> parameters = new HashMap<>();
for (Map.Entry<String, Deque<String>> entry : exchange.getQueryParameters().entrySet()) {
if (Objects.nonNull(entry.getValue().peekFirst())) {
parameters.put(entry.getKey(), entry.getValue().peekFirst());
}
}
// TODO test with missing parameters to see behaviour
String sender = parameters.get("sender");
String medium = parameters.get("medium");
String address = parameters.get("address");
String roomId = parameters.get("room_id");
IThreePidInvite invite = new ThreePidInvite(MatrixID.asAcceptable(sender), medium, address, roomId, parameters);
IThreePidInviteReply reply = invMgr.storeInvite(invite);
respondJson(exchange, new ThreePidInviteReplyIO(reply, keyMgr.getPublicKeyBase64(keyMgr.getCurrentIndex()), cfg.getPublicUrl()));
}
}

View File

@@ -0,0 +1,54 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.profile.v1;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix._MatrixID;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.profile.ProfileManager;
import io.undertow.server.HttpServerExchange;
import org.apache.http.client.methods.HttpGet;
import java.net.URI;
import java.util.Optional;
public class InternalProfileHandler extends ProfileHandler {
public static final String Path = "/_matrix-internal/profile/v1/{" + UserID + "}";
public InternalProfileHandler(ProfileManager mgr) {
super(mgr);
}
@Override
public void handleRequest(HttpServerExchange exchange) {
String userId = getQueryParameter(exchange, UserID);
_MatrixID mxId = MatrixID.asAcceptable(userId);
URI target = URI.create(exchange.getRequestURI());
Optional<String> accessTokenOpt = findAccessToken(exchange);
HttpGet reqOut = new HttpGet(target);
accessTokenOpt.ifPresent(accessToken -> reqOut.addHeader(headerName, headerValuePrefix + accessToken));
respond(exchange, GsonUtil.makeObj("roles", GsonUtil.asArray(mgr.getRoles(mxId))));
}
}

View File

@@ -0,0 +1,59 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.profile.v1;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix._MatrixID;
import io.kamax.mxisd.http.undertow.handler.HomeserverProxyHandler;
import io.kamax.mxisd.profile.ProfileManager;
import io.kamax.mxisd.proxy.Response;
import io.undertow.server.HttpServerExchange;
import org.apache.http.client.methods.HttpGet;
import java.net.URI;
import java.util.Optional;
public class ProfileHandler extends HomeserverProxyHandler {
public static final String UserID = "userId";
public static final String Path = "/_matrix/client/r0/profile/{" + UserID + "}";
protected ProfileManager mgr;
public ProfileHandler(ProfileManager mgr) {
this.mgr = mgr;
}
@Override
public void handleRequest(HttpServerExchange exchange) {
String userId = getQueryParameter(exchange, UserID);
_MatrixID mxId = MatrixID.asAcceptable(userId);
URI target = URI.create(exchange.getRequestURL());
Optional<String> accessTokenOpt = findAccessToken(exchange);
HttpGet reqOut = new HttpGet(target);
accessTokenOpt.ifPresent(accessToken -> reqOut.addHeader(headerName, headerValuePrefix + accessToken));
Response res = mgr.enhance(mxId, reqOut);
respond(exchange, res);
}
}

View File

@@ -0,0 +1,43 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sarl
*
* https://www.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.http.undertow.handler.status;
import com.google.gson.JsonObject;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.undertow.server.HttpServerExchange;
public class StatusHandler extends BasicHttpHandler {
public static final String Path = "/_matrix/identity/status";
@Override
public void handleRequest(HttpServerExchange exchange) {
// TODO link to backend
JsonObject status = new JsonObject();
status.addProperty("health", "OK");
JsonObject obj = new JsonObject();
obj.add("status", status);
respond(exchange, obj);
}
}