Add account handlers.

This commit is contained in:
Anatoly Sablin
2019-09-30 23:53:38 +03:00
parent 614b3440e2
commit 5521c0c338
8 changed files with 217 additions and 36 deletions

View File

@@ -33,6 +33,9 @@ import io.kamax.mxisd.http.undertow.handler.auth.RestAuthHandler;
import io.kamax.mxisd.http.undertow.handler.auth.v1.LoginGetHandler;
import io.kamax.mxisd.http.undertow.handler.auth.v1.LoginHandler;
import io.kamax.mxisd.http.undertow.handler.auth.v1.LoginPostHandler;
import io.kamax.mxisd.http.undertow.handler.auth.v2.AccountGetUserInfoHandler;
import io.kamax.mxisd.http.undertow.handler.auth.v2.AccountLogoutHandler;
import io.kamax.mxisd.http.undertow.handler.auth.v2.AccountRegisterHandler;
import io.kamax.mxisd.http.undertow.handler.directory.v1.UserDirectorySearchHandler;
import io.kamax.mxisd.http.undertow.handler.identity.share.EphemeralKeyIsValidHandler;
import io.kamax.mxisd.http.undertow.handler.identity.share.HelloHandler;
@@ -105,6 +108,11 @@ public class HttpMxisd {
.post(LoginHandler.Path, SaneHandler.around(new LoginPostHandler(m.getAuth())))
.post(RestAuthHandler.Path, SaneHandler.around(new RestAuthHandler(m.getAuth())))
// Account endpoints
.post(AccountRegisterHandler.Path, SaneHandler.around(new AccountRegisterHandler(m.getAccMgr())))
.get(AccountGetUserInfoHandler.Path, SaneHandler.around(new AccountGetUserInfoHandler(m.getAccMgr())))
.post(AccountLogoutHandler.Path, SaneHandler.around(new AccountLogoutHandler(m.getAccMgr())))
// Directory endpoints
.post(UserDirectorySearchHandler.Path, SaneHandler.around(new UserDirectorySearchHandler(m.getDirectory())))

View File

@@ -21,6 +21,7 @@
package io.kamax.mxisd;
import io.kamax.mxisd.as.AppSvcManager;
import io.kamax.mxisd.auth.AccountManager;
import io.kamax.mxisd.auth.AuthManager;
import io.kamax.mxisd.auth.AuthProviders;
import io.kamax.mxisd.backend.IdentityStoreSupplier;
@@ -85,6 +86,7 @@ public class Mxisd {
private SessionManager sessMgr;
private NotificationManager notifMgr;
private RegistrationManager regMgr;
private AccountManager accMgr;
// HS-specific classes
private Synapse synapse;
@@ -124,6 +126,7 @@ public class Mxisd {
dirMgr = new DirectoryManager(cfg.getDirectory(), clientDns, httpClient, DirectoryProviders.get());
regMgr = new RegistrationManager(cfg.getRegister(), httpClient, clientDns, invMgr);
asHander = new AppSvcManager(this);
accMgr = new AccountManager(store, resolver, getHttpClient(), cfg.getAccountConfig(), cfg.getMatrix());
}
public MxisdConfig getConfig() {
@@ -194,6 +197,10 @@ public class Mxisd {
return synapse;
}
public AccountManager getAccMgr() {
return accMgr;
}
public void start() {
build();
}

View File

@@ -1,7 +1,11 @@
package io.kamax.mxisd.auth;
import com.google.gson.JsonObject;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.config.AccountConfig;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.exception.BadRequestException;
import io.kamax.mxisd.exception.InvalidCredentialsException;
import io.kamax.mxisd.exception.NotFoundException;
import io.kamax.mxisd.matrix.HomeserverFederationResolver;
@@ -27,12 +31,16 @@ public class AccountManager {
private final IStorage storage;
private final HomeserverFederationResolver resolver;
private final CloseableHttpClient httpClient;
private final AccountConfig accountConfig;
private final MatrixConfig matrixConfig;
public AccountManager(IStorage storage, HomeserverFederationResolver resolver,
CloseableHttpClient httpClient) {
CloseableHttpClient httpClient, AccountConfig accountConfig, MatrixConfig matrixConfig) {
this.storage = storage;
this.resolver = resolver;
this.httpClient = httpClient;
this.accountConfig = accountConfig;
this.matrixConfig = matrixConfig;
}
public String register(OpenIdToken openIdToken) {
@@ -40,6 +48,20 @@ public class AccountManager {
Objects.requireNonNull(openIdToken.getTokenType(), "Missing required token type");
Objects.requireNonNull(openIdToken.getMatrixServerName(), "Missing required matrix domain");
String userId = getUserId(openIdToken);
String token = UUID.randomUUID().toString();
AccountDao account = new AccountDao(openIdToken.getAccessToken(), openIdToken.getTokenType(),
openIdToken.getMatrixServerName(), openIdToken.getExpiredIn(),
Instant.now().getEpochSecond(), userId, token);
storage.insertToken(account);
LOGGER.info("User {} registered", userId);
return token;
}
private String getUserId(OpenIdToken openIdToken) {
String homeserverURL = resolver.resolve(openIdToken.getMatrixServerName()).toString();
HttpGet getUserInfo = new HttpGet(
"https://" + homeserverURL + "/_matrix/federation/v1/openid/userinfo?access_token=" + openIdToken.getAccessToken());
@@ -58,12 +80,30 @@ public class AccountManager {
throw new InvalidCredentialsException();
}
String token = UUID.randomUUID().toString();
AccountDao account = new AccountDao(openIdToken.getAccessToken(), openIdToken.getTokenType(),
openIdToken.getMatrixServerName(), openIdToken.getExpiredIn(),
Instant.now().getEpochSecond(), userId, token);
storage.insertToken(account);
return token;
checkMXID(userId);
return userId;
}
private void checkMXID(String userId) {
MatrixID mxid;
try {
mxid = MatrixID.asValid(userId);
} catch (IllegalArgumentException e) {
LOGGER.error("Wrong MXID: " + userId, e);
throw new BadRequestException("Wrong MXID");
}
if (getAccountConfig().isAllowOnlyTrustDomains()) {
LOGGER.info("Allow registration only for trust domain.");
if (getMatrixConfig().getDomain().equals(mxid.getDomain())) {
LOGGER.info("Allow user {} to registration", userId);
} else {
LOGGER.error("Deny user {} to registration", userId);
throw new InvalidCredentialsException();
}
} else {
LOGGER.info("Allow registration from any server.");
}
}
public String getUserId(String token) {
@@ -75,4 +115,12 @@ public class AccountManager {
LOGGER.info("Logout: {}", userId);
storage.deleteToken(token);
}
public AccountConfig getAccountConfig() {
return accountConfig;
}
public MatrixConfig getMatrixConfig() {
return matrixConfig;
}
}

View File

@@ -0,0 +1,24 @@
package io.kamax.mxisd.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AccountConfig {
private final static Logger log = LoggerFactory.getLogger(DirectoryConfig.class);
private boolean allowOnlyTrustDomains = true;
public boolean isAllowOnlyTrustDomains() {
return allowOnlyTrustDomains;
}
public void setAllowOnlyTrustDomains(boolean allowOnlyTrustDomains) {
this.allowOnlyTrustDomains = allowOnlyTrustDomains;
}
public void build() {
log.info("--- Account config ---");
log.info("Allow registration only for trust domain: {}", isAllowOnlyTrustDomains());
}
}

View File

@@ -92,6 +92,7 @@ public class MxisdConfig {
private AppServiceConfig appsvc = new AppServiceConfig();
private AuthenticationConfig auth = new AuthenticationConfig();
private DirectoryConfig directory = new DirectoryConfig();
private AccountConfig accountConfig = new AccountConfig();
private Dns dns = new Dns();
private ExecConfig exec = new ExecConfig();
private FirebaseConfig firebase = new FirebaseConfig();
@@ -131,6 +132,14 @@ public class MxisdConfig {
this.auth = auth;
}
public AccountConfig getAccountConfig() {
return accountConfig;
}
public void setAccountConfig(AccountConfig accountConfig) {
this.accountConfig = accountConfig;
}
public DirectoryConfig getDirectory() {
return directory;
}
@@ -330,6 +339,7 @@ public class MxisdConfig {
getAppsvc().build();
getAuth().build();
getAccountConfig().build();
getDirectory().build();
getExec().build();
getFirebase().build();

View File

@@ -0,0 +1,53 @@
/*
* 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.v2;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.auth.AccountManager;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.undertow.server.HttpServerExchange;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AccountGetUserInfoHandler extends BasicHttpHandler {
public static final String Path = "/_matrix/identity/v2/account";
private static final Logger log = LoggerFactory.getLogger(AccountGetUserInfoHandler.class);
private final AccountManager accountManager;
public AccountGetUserInfoHandler(AccountManager accountManager) {
this.accountManager = accountManager;
}
@Override
public void handleRequest(HttpServerExchange exchange) {
String token = getQueryParameter(exchange, "access_token");
if (token == null) {
token = getAccessToken(exchange);
}
String userId = accountManager.getUserId(token);
respond(exchange, GsonUtil.makeObj("user_id", userId));
}
}

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.v2;
import io.kamax.mxisd.auth.AccountManager;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.undertow.server.HttpServerExchange;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AccountLogoutHandler extends BasicHttpHandler {
public static final String Path = "/_matrix/identity/v2/account";
private static final Logger log = LoggerFactory.getLogger(AccountLogoutHandler.class);
private final AccountManager accountManager;
public AccountLogoutHandler(AccountManager accountManager) {
this.accountManager = accountManager;
}
@Override
public void handleRequest(HttpServerExchange exchange) {
String token = getQueryParameter(exchange, "access_token");
if (token == null) {
token = getAccessToken(exchange);
}
accountManager.logout(token);
respondJson(exchange, "{}");
}
}

View File

@@ -20,13 +20,9 @@
package io.kamax.mxisd.http.undertow.handler.auth.v2;
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.auth.AccountManager;
import io.kamax.mxisd.auth.OpenIdToken;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.undertow.server.HttpServerExchange;
import org.slf4j.Logger;
@@ -38,33 +34,16 @@ public class AccountRegisterHandler extends BasicHttpHandler {
private static final Logger log = LoggerFactory.getLogger(AccountRegisterHandler.class);
private AuthManager mgr;
private final AccountManager accountManager;
public AccountRegisterHandler(AuthManager mgr) {
this.mgr = mgr;
public AccountRegisterHandler(AccountManager accountManager) {
this.accountManager = accountManager;
}
@Override
public void handleRequest(HttpServerExchange exchange) {
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));
OpenIdToken openIdToken = parseJsonTo(exchange, OpenIdToken.class);
String token = accountManager.register(openIdToken);
respond(exchange, GsonUtil.makeObj("token", token));
}
}