Add support for username rewrite (Fix #103)
This commit is contained in:
@@ -74,7 +74,15 @@ See your Identity store [documentation](../stores/README.md) on how to enable th
|
||||
|
||||
|
||||
## Advanced
|
||||
The Authentication feature allows users to login to their Homeserver by using their 3PIDs in a configured Identity store.
|
||||
The Authentication feature allows users to:
|
||||
- Rewrite usernames matching a pattern to be mapped to another username via a 3PID.
|
||||
- login to their Homeserver by using their 3PIDs in a configured Identity store.
|
||||
|
||||
This feature also allows to work around the following issues:
|
||||
- Lowercase all usernames for synapse, allowing case-insensitive login
|
||||
- Unable to login on synapse if username is numerical
|
||||
- Any generic transformation of username prior to sending to synapse, bypassing the restriction that password providers
|
||||
cannot change the localpart being authenticated.
|
||||
|
||||
### Overview
|
||||
This is performed by intercepting the Homeserver endpoint `/_matrix/client/r0/login` as depicted below:
|
||||
@@ -109,10 +117,10 @@ Steps of user authentication using a 3PID:
|
||||
4. The response from the Homeserver is sent back to the client, believing it was the HS which directly answered.
|
||||
|
||||
### Requirements
|
||||
- [Basic Authentication configured and working](#basic)
|
||||
- Reverse proxy setup
|
||||
- Homeserver
|
||||
- Compatible [Identity store](../stores/README.md)
|
||||
- [Basic Authentication configured and working](#basic)
|
||||
- Client and Homeserver using the [C2S API r0.4.x](https://matrix.org/docs/spec/client_server/r0.4.0.html) or later
|
||||
- Reverse proxy setup
|
||||
|
||||
### Configuration
|
||||
#### Reverse Proxy
|
||||
@@ -153,3 +161,40 @@ In case the hostname is the same as your Matrix domain and `server.name` is not
|
||||
`matrix.domain` and will still probably have the correct value.
|
||||
|
||||
`value` is the base internal URL of the Homeserver, without any `/_matrix/..` or trailing `/`.
|
||||
|
||||
#### Username rewrite
|
||||
In mxisd config:
|
||||
```yaml
|
||||
auth:
|
||||
rewrite:
|
||||
user:
|
||||
rules:
|
||||
- regex: <your regexp>
|
||||
medium: 'your.custom.medium.type'
|
||||
```
|
||||
`rules` takes a list of rules. Rules have two properties:
|
||||
- `regexp`: The regex pattern to match. This **MUST** match the full string. See [Java regex](https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html) for syntax.
|
||||
- `medium`: Custom 3PID type that will be used in the 3PID lookup. This can be anything you want and needs to be supported
|
||||
by your Identity store config and/or code.
|
||||
|
||||
Rules are matched in listed order.
|
||||
|
||||
Common regexp patterns:
|
||||
- Numerical usernames: `[0-9]+`
|
||||
|
||||
##### LDAP Example
|
||||
If your users use their numerical employee IDs, which cannot be used with synapse, you can make it work with (relevant config only):
|
||||
```yaml
|
||||
auth:
|
||||
rewrite:
|
||||
user:
|
||||
rules:
|
||||
- regex: '[0-9]+'
|
||||
medium: 'kmx.employee.id'
|
||||
|
||||
ldap:
|
||||
attribute:
|
||||
threepid:
|
||||
kmx.employee.id:
|
||||
- 'ldapAttributeForEmployeeId'
|
||||
```
|
||||
|
@@ -20,37 +20,95 @@
|
||||
|
||||
package io.kamax.mxisd.auth;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.i18n.phonenumbers.NumberParseException;
|
||||
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
||||
import com.google.i18n.phonenumbers.Phonenumber;
|
||||
import io.kamax.matrix.MatrixID;
|
||||
import io.kamax.matrix.ThreePid;
|
||||
import io.kamax.matrix._MatrixID;
|
||||
import io.kamax.matrix._ThreePid;
|
||||
import io.kamax.matrix.json.GsonUtil;
|
||||
import io.kamax.mxisd.UserIdType;
|
||||
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
|
||||
import io.kamax.mxisd.auth.provider.BackendAuthResult;
|
||||
import io.kamax.mxisd.config.AuthenticationConfig;
|
||||
import io.kamax.mxisd.config.MatrixConfig;
|
||||
import io.kamax.mxisd.dns.ClientDnsOverwrite;
|
||||
import io.kamax.mxisd.exception.RemoteLoginException;
|
||||
import io.kamax.mxisd.invitation.InvitationManager;
|
||||
import io.kamax.mxisd.lookup.ThreePidMapping;
|
||||
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
|
||||
import io.kamax.mxisd.util.RestClientUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
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;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@Service
|
||||
public class AuthManager {
|
||||
|
||||
private Logger log = LoggerFactory.getLogger(AuthManager.class);
|
||||
private static final String TypeKey = "type";
|
||||
private static final String UserKey = "user";
|
||||
private static final String IdentifierKey = "identifier";
|
||||
private static final String ThreepidMediumKey = "medium";
|
||||
private static final String ThreepidAddressKey = "address";
|
||||
private static final String UserIdTypeValue = "m.id.user";
|
||||
private static final String ThreepidTypeValue = "m.id.thirdparty";
|
||||
|
||||
@Autowired
|
||||
private List<AuthenticatorProvider> providers = new ArrayList<>();
|
||||
private final Logger log = LoggerFactory.getLogger(AuthManager.class);
|
||||
private final Gson gson = GsonUtil.get();
|
||||
|
||||
@Autowired
|
||||
private List<AuthenticatorProvider> providers;
|
||||
private MatrixConfig mxCfg;
|
||||
private AuthenticationConfig cfg;
|
||||
private InvitationManager invMgr;
|
||||
private ClientDnsOverwrite dns;
|
||||
private LookupStrategy strategy;
|
||||
private CloseableHttpClient client;
|
||||
|
||||
@Autowired
|
||||
private InvitationManager invMgr;
|
||||
public AuthManager(
|
||||
AuthenticationConfig cfg,
|
||||
MatrixConfig mxCfg,
|
||||
List<AuthenticatorProvider> providers,
|
||||
LookupStrategy strategy,
|
||||
InvitationManager invMgr,
|
||||
ClientDnsOverwrite dns,
|
||||
CloseableHttpClient client
|
||||
) {
|
||||
this.cfg = cfg;
|
||||
this.mxCfg = mxCfg;
|
||||
this.providers = new ArrayList<>(providers);
|
||||
this.strategy = strategy;
|
||||
this.invMgr = invMgr;
|
||||
this.dns = dns;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public String resolveProxyUrl(URI target) {
|
||||
URIBuilder builder = dns.transform(target);
|
||||
String urlToLogin = builder.toString();
|
||||
log.info("Proxy resolution: {} to {}", target.toString(), urlToLogin);
|
||||
return urlToLogin;
|
||||
}
|
||||
|
||||
public UserAuthResult authenticate(String id, String password) {
|
||||
_MatrixID mxid = MatrixID.asAcceptable(id);
|
||||
@@ -92,4 +150,128 @@ public class AuthManager {
|
||||
return new UserAuthResult().failure();
|
||||
}
|
||||
|
||||
public String proxyLogin(URI target, String body) {
|
||||
JsonObject reqJsonObject = io.kamax.matrix.json.GsonUtil.parseObj(body);
|
||||
|
||||
GsonUtil.findObj(reqJsonObject, IdentifierKey).ifPresent(obj -> {
|
||||
GsonUtil.findString(obj, TypeKey).ifPresent(type -> {
|
||||
if (StringUtils.equals(type, UserIdTypeValue)) {
|
||||
log.info("Login request is User ID type");
|
||||
|
||||
if (cfg.getRewrite().getUser().getRules().isEmpty()) {
|
||||
log.info("No User ID rewrite rules to apply");
|
||||
} else {
|
||||
log.info("User ID rewrite rules: checking for a match");
|
||||
|
||||
String userId = GsonUtil.getStringOrThrow(obj, UserKey);
|
||||
for (AuthenticationConfig.Rule m : cfg.getRewrite().getUser().getRules()) {
|
||||
if (m.getPattern().matcher(userId).matches()) {
|
||||
log.info("Found matching pattern, resolving to 3PID with medium {}", m.getMedium());
|
||||
|
||||
// Remove deprecated login info on the top object if exists to avoid duplication
|
||||
reqJsonObject.remove(UserKey);
|
||||
obj.addProperty(TypeKey, ThreepidTypeValue);
|
||||
obj.addProperty(ThreepidMediumKey, m.getMedium());
|
||||
obj.addProperty(ThreepidAddressKey, userId);
|
||||
|
||||
log.info("Rewrite to 3PID done");
|
||||
}
|
||||
}
|
||||
|
||||
log.info("User ID rewrite rules: done checking rules");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
GsonUtil.findObj(reqJsonObject, IdentifierKey).ifPresent(obj -> {
|
||||
GsonUtil.findString(obj, TypeKey).ifPresent(type -> {
|
||||
if (StringUtils.equals(type, ThreepidTypeValue)) {
|
||||
// Remove deprecated login info if exists to avoid duplication
|
||||
reqJsonObject.remove(ThreepidMediumKey);
|
||||
reqJsonObject.remove(ThreepidAddressKey);
|
||||
|
||||
GsonUtil.findPrimitive(obj, ThreepidMediumKey).ifPresent(medium -> {
|
||||
GsonUtil.findPrimitive(obj, ThreepidAddressKey).ifPresent(address -> {
|
||||
log.info("Login request with medium '{}' and address '{}'", medium.getAsString(), address.getAsString());
|
||||
strategy.findLocal(medium.getAsString(), address.getAsString()).ifPresent(lookupDataOpt -> {
|
||||
obj.remove(ThreepidMediumKey);
|
||||
obj.remove(ThreepidAddressKey);
|
||||
obj.addProperty(TypeKey, UserIdTypeValue);
|
||||
obj.addProperty(UserKey, lookupDataOpt.getMxid().getLocalPart());
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (StringUtils.equals(type, "m.id.phone")) {
|
||||
// Remove deprecated login info if exists to avoid duplication
|
||||
reqJsonObject.remove(ThreepidMediumKey);
|
||||
reqJsonObject.remove(ThreepidAddressKey);
|
||||
|
||||
GsonUtil.findPrimitive(obj, "number").ifPresent(number -> {
|
||||
GsonUtil.findPrimitive(obj, "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 msisdn = phoneUtil.format(phoneNumber, PhoneNumberUtil.PhoneNumberFormat.E164).replace("+", "");
|
||||
String medium = "msisdn";
|
||||
strategy.findLocal(medium, msisdn).ifPresent(lookupDataOpt -> {
|
||||
obj.remove("country");
|
||||
obj.remove("number");
|
||||
obj.addProperty(TypeKey, UserIdTypeValue);
|
||||
obj.addProperty(UserKey, lookupDataOpt.getMxid().getLocalPart());
|
||||
});
|
||||
} catch (NumberParseException e) {
|
||||
log.error("Not a valid phone number");
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// invoke 'login' on homeserver
|
||||
HttpPost httpPost = RestClientUtils.post(resolveProxyUrl(target), 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
|
||||
HttpEntity entity = httpResponse.getEntity();
|
||||
if (Objects.isNull(entity)) {
|
||||
log.warn("Expected HS to return data but got nothing");
|
||||
return "";
|
||||
} else {
|
||||
return IOUtils.toString(httpResponse.getEntity().getContent(), httpResponse.getEntity().getContentType().getValue());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
110
src/main/java/io/kamax/mxisd/config/AuthenticationConfig.java
Normal file
110
src/main/java/io/kamax/mxisd/config/AuthenticationConfig.java
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "auth")
|
||||
public class AuthenticationConfig {
|
||||
|
||||
public static class Rule {
|
||||
|
||||
private String regex;
|
||||
private transient Pattern pattern;
|
||||
private String medium;
|
||||
|
||||
public String getRegex() {
|
||||
return regex;
|
||||
}
|
||||
|
||||
public void setRegex(String regex) {
|
||||
this.regex = regex;
|
||||
}
|
||||
|
||||
public Pattern getPattern() {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
public void setPattern(Pattern pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
public String getMedium() {
|
||||
return medium;
|
||||
}
|
||||
|
||||
public void setMedium(String medium) {
|
||||
this.medium = medium;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class User {
|
||||
|
||||
private List<Rule> rules = new ArrayList<>();
|
||||
|
||||
public List<Rule> getRules() {
|
||||
return rules;
|
||||
}
|
||||
|
||||
public void setRules(List<Rule> mappings) {
|
||||
this.rules = mappings;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Rewrite {
|
||||
|
||||
private User user = new User();
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Rewrite rewrite = new Rewrite();
|
||||
|
||||
public Rewrite getRewrite() {
|
||||
return rewrite;
|
||||
}
|
||||
|
||||
public void setRewrite(Rewrite rewrite) {
|
||||
this.rewrite = rewrite;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void build() {
|
||||
getRewrite().getUser().getRules().forEach(mapping -> mapping.setPattern(Pattern.compile(mapping.getRegex())));
|
||||
}
|
||||
|
||||
}
|
@@ -20,25 +20,18 @@
|
||||
|
||||
package io.kamax.mxisd.controller.auth.v1;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.google.i18n.phonenumbers.NumberParseException;
|
||||
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
||||
import com.google.i18n.phonenumbers.Phonenumber;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
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.commons.io.IOUtils;
|
||||
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;
|
||||
@@ -54,10 +47,11 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@RestController
|
||||
@CrossOrigin
|
||||
@RequestMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
@RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public class AuthController {
|
||||
|
||||
// TODO export into SDK
|
||||
@@ -71,23 +65,9 @@ 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 {
|
||||
@@ -120,7 +100,9 @@ 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)))) {
|
||||
URI target = URI.create(req.getRequestURL().toString());
|
||||
|
||||
try (CloseableHttpResponse hsResponse = client.execute(new HttpGet(mgr.resolveProxyUrl(target)))) {
|
||||
res.setStatus(hsResponse.getStatusLine().getStatusCode());
|
||||
return EntityUtils.toString(hsResponse.getEntity());
|
||||
} catch (IOException e) {
|
||||
@@ -130,98 +112,11 @@ public class AuthController {
|
||||
|
||||
@RequestMapping(value = logV1Url, method = RequestMethod.POST)
|
||||
public String login(HttpServletRequest req) {
|
||||
URI target = URI.create(req.getRequestURL().toString());
|
||||
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);
|
||||
}
|
||||
return mgr.proxyLogin(target, IOUtils.toString(req.getInputStream(), StandardCharsets.UTF_8));
|
||||
} catch (IOException e) {
|
||||
log.error("Unable to read input data from client");
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user