3PID authentication (#60)

Fix for #49
This commit is contained in:
adrnam
2018-03-08 18:29:03 +01:00
committed by Max Dor
parent 1db76139a9
commit 61fec4aec7
12 changed files with 646 additions and 12 deletions

View File

@@ -54,6 +54,16 @@ public class DefaultExceptionHandler {
return gson.toJson(obj);
}
@ExceptionHandler(RemoteLoginException.class)
public String handle(HttpServletRequest request, HttpServletResponse response, RemoteLoginException e) {
if (e.getErrorBodyMsgResp() != null) {
response.setStatus(e.getStatus());
log.info("Request {} {} - Error {}: {}", request.getMethod(), request.getRequestURL(), e.getErrorCode(), e.getError());
return gson.toJson(e.getErrorBodyMsgResp());
}
return handleGeneric(request, response, e);
}
@ExceptionHandler(InternalServerError.class)
public String handle(HttpServletRequest request, HttpServletResponse response, InternalServerError e) {
if (StringUtils.isNotBlank(e.getInternalReason())) {

View File

@@ -20,15 +20,27 @@
package io.kamax.mxisd.controller.auth.v1;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.*;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber;
import io.kamax.mxisd.auth.AuthManager;
import io.kamax.mxisd.auth.UserAuthResult;
import io.kamax.mxisd.controller.auth.v1.io.CredentialsValidationResponse;
import io.kamax.mxisd.dns.ClientDnsOverwrite;
import io.kamax.mxisd.exception.JsonMemberNotFoundException;
import io.kamax.mxisd.exception.RemoteLoginException;
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
import io.kamax.mxisd.util.GsonParser;
import io.kamax.mxisd.util.GsonUtil;
import io.kamax.mxisd.util.RestClientUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -39,13 +51,18 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URI;
@RestController
@CrossOrigin
@RequestMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class AuthController {
// TODO export into SDK
private static final String logV1Url = "/_matrix/client/r0/login";
private Logger log = LoggerFactory.getLogger(AuthController.class);
private Gson gson = GsonUtil.build();
@@ -54,6 +71,23 @@ public class AuthController {
@Autowired
private AuthManager mgr;
@Autowired
private LookupStrategy strategy;
@Autowired
private ClientDnsOverwrite dns;
@Autowired
private CloseableHttpClient client;
private String resolveProxyUrl(HttpServletRequest req) {
URI target = URI.create(req.getRequestURL().toString());
URIBuilder builder = dns.transform(target);
String urlToLogin = builder.toString();
log.info("Proxy resolution: {} to {}", target.toString(), urlToLogin);
return urlToLogin;
}
@RequestMapping(value = "/_matrix-internal/identity/v1/check_credentials", method = RequestMethod.POST)
public String checkCredentials(HttpServletRequest req) {
try {
@@ -84,4 +118,112 @@ public class AuthController {
}
}
@RequestMapping(value = logV1Url, method = RequestMethod.GET)
public String getLogin(HttpServletRequest req, HttpServletResponse res) {
try (CloseableHttpResponse hsResponse = client.execute(new HttpGet(resolveProxyUrl(req)))) {
res.setStatus(hsResponse.getStatusLine().getStatusCode());
return EntityUtils.toString(hsResponse.getEntity());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@RequestMapping(value = logV1Url, method = RequestMethod.POST)
public String login(HttpServletRequest req) {
try {
JsonObject reqJsonObject = parser.parse(req.getInputStream());
// find 3PID in main object
GsonUtil.findPrimitive(reqJsonObject, "medium").ifPresent(medium -> {
GsonUtil.findPrimitive(reqJsonObject, "address").ifPresent(address -> {
log.info("Login request with medium '{}' and address '{}'", medium.getAsString(), address.getAsString());
strategy.findLocal(medium.getAsString(), address.getAsString()).ifPresent(lookupDataOpt -> {
reqJsonObject.addProperty("user", lookupDataOpt.getMxid().getLocalPart());
reqJsonObject.remove("medium");
reqJsonObject.remove("address");
});
});
});
// find 3PID in 'identifier' object
GsonUtil.findObj(reqJsonObject, "identifier").ifPresent(identifier -> {
GsonUtil.findPrimitive(identifier, "type").ifPresent(type -> {
if (StringUtils.equals(type.getAsString(), "m.id.thirdparty")) {
GsonUtil.findPrimitive(identifier, "medium").ifPresent(medium -> {
GsonUtil.findPrimitive(identifier, "address").ifPresent(address -> {
log.info("Login request with medium '{}' and address '{}'", medium.getAsString(), address.getAsString());
strategy.findLocal(medium.getAsString(), address.getAsString()).ifPresent(lookupDataOpt -> {
identifier.addProperty("type", "m.id.user");
identifier.addProperty("user", lookupDataOpt.getMxid().getLocalPart());
identifier.remove("medium");
identifier.remove("address");
});
});
});
}
if (StringUtils.equals(type.getAsString(), "m.id.phone")) {
GsonUtil.findPrimitive(identifier, "number").ifPresent(number -> {
GsonUtil.findPrimitive(identifier, "country").ifPresent(country -> {
log.info("Login request with phone '{}'-'{}'", country.getAsString(), number.getAsString());
try {
PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
Phonenumber.PhoneNumber phoneNumber = phoneUtil.parse(number.getAsString(), country.getAsString());
String canon_phoneNumber = phoneUtil.format(phoneNumber, PhoneNumberUtil.PhoneNumberFormat.E164).replace("+", "");
String medium = "msisdn";
strategy.findLocal(medium, canon_phoneNumber).ifPresent(lookupDataOpt -> {
identifier.addProperty("type", "m.id.user");
identifier.addProperty("user", lookupDataOpt.getMxid().getLocalPart());
identifier.remove("country");
identifier.remove("number");
});
} catch (NumberParseException e) {
throw new RuntimeException(e);
}
});
});
}
});
});
// invoke 'login' on homeserver
HttpPost httpPost = RestClientUtils.post(resolveProxyUrl(req), gson, reqJsonObject);
try (CloseableHttpResponse httpResponse = client.execute(httpPost)) {
// check http status
int status = httpResponse.getStatusLine().getStatusCode();
log.info("http status = {}", status);
if (status != 200) {
// try to get possible json error message from response
// otherwise just get returned plain error message
String errcode = String.valueOf(httpResponse.getStatusLine().getStatusCode());
String error = EntityUtils.toString(httpResponse.getEntity());
if (httpResponse.getEntity() != null) {
try {
JsonObject bodyJson = new JsonParser().parse(error).getAsJsonObject();
if (bodyJson.has("errcode")) {
errcode = bodyJson.get("errcode").getAsString();
}
if (bodyJson.has("error")) {
error = bodyJson.get("error").getAsString();
}
throw new RemoteLoginException(status, errcode, error, bodyJson);
} catch (JsonSyntaxException e) {
log.warn("Response body is not JSON");
}
}
throw new RemoteLoginException(status, errcode, error);
}
/// return response
JsonObject respJsonObject = parser.parseOptional(httpResponse).get();
return gson.toJson(respJsonObject);
} catch (IOException e) {
throw new RuntimeException(e);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}