Merge branch 'MSC2140'
# Conflicts: # src/main/java/io/kamax/mxisd/session/SessionManager.java
This commit is contained in:
28
build.gradle
28
build.gradle
@@ -78,7 +78,7 @@ buildscript {
|
||||
|
||||
dependencies {
|
||||
classpath 'com.github.jengelman.gradle.plugins:shadow:5.1.0'
|
||||
classpath 'com.github.ben-manes:gradle-versions-plugin:0.21.0'
|
||||
classpath 'com.github.ben-manes:gradle-versions-plugin:0.27.0'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,12 +94,12 @@ dependencies {
|
||||
compile 'commons-io:commons-io:2.6'
|
||||
|
||||
// Config management
|
||||
compile 'org.yaml:snakeyaml:1.24'
|
||||
compile 'org.yaml:snakeyaml:1.25'
|
||||
|
||||
// Dependencies from old Matrix-java-sdk
|
||||
compile 'org.apache.commons:commons-lang3:3.9'
|
||||
compile 'com.squareup.okhttp3:okhttp:4.0.1'
|
||||
compile 'commons-codec:commons-codec:1.12'
|
||||
compile 'com.squareup.okhttp3:okhttp:4.2.2'
|
||||
compile 'commons-codec:commons-codec:1.13'
|
||||
|
||||
// ORMLite
|
||||
compile 'com.j256.ormlite:ormlite-jdbc:5.1'
|
||||
@@ -114,10 +114,10 @@ dependencies {
|
||||
compile 'dnsjava:dnsjava:2.1.9'
|
||||
|
||||
// HTTP connections
|
||||
compile 'org.apache.httpcomponents:httpclient:4.5.9'
|
||||
compile 'org.apache.httpcomponents:httpclient:4.5.10'
|
||||
|
||||
// Phone numbers validation
|
||||
compile 'com.googlecode.libphonenumber:libphonenumber:8.10.15'
|
||||
compile 'com.googlecode.libphonenumber:libphonenumber:8.10.22'
|
||||
|
||||
// E-mail sending
|
||||
compile 'javax.mail:javax.mail-api:1.6.2'
|
||||
@@ -133,13 +133,13 @@ dependencies {
|
||||
compile 'org.xerial:sqlite-jdbc:3.28.0'
|
||||
|
||||
// PostgreSQL
|
||||
compile 'org.postgresql:postgresql:42.2.6'
|
||||
compile 'org.postgresql:postgresql:42.2.8'
|
||||
|
||||
// MariaDB/MySQL
|
||||
compile 'org.mariadb.jdbc:mariadb-java-client:2.4.2'
|
||||
compile 'org.mariadb.jdbc:mariadb-java-client:2.5.1'
|
||||
|
||||
// Twilio SDK for SMS
|
||||
compile 'com.twilio.sdk:twilio:7.40.1'
|
||||
compile 'com.twilio.sdk:twilio:7.45.0'
|
||||
|
||||
// SendGrid SDK to send emails from GCE
|
||||
compile 'com.sendgrid:sendgrid-java:2.2.2'
|
||||
@@ -148,15 +148,15 @@ dependencies {
|
||||
compile 'org.zeroturnaround:zt-exec:1.11'
|
||||
|
||||
// HTTP server
|
||||
compile 'io.undertow:undertow-core:2.0.22.Final'
|
||||
compile 'io.undertow:undertow-core:2.0.27.Final'
|
||||
|
||||
// Command parser for AS interface
|
||||
implementation 'commons-cli:commons-cli:1.4'
|
||||
|
||||
testCompile 'junit:junit:4.13-beta-3'
|
||||
testCompile 'com.github.tomakehurst:wiremock:2.24.0'
|
||||
testCompile 'com.unboundid:unboundid-ldapsdk:4.0.11'
|
||||
testCompile 'com.icegreen:greenmail:1.5.10'
|
||||
testCompile 'junit:junit:4.13-rc-1'
|
||||
testCompile 'com.github.tomakehurst:wiremock:2.25.1'
|
||||
testCompile 'com.unboundid:unboundid-ldapsdk:4.0.12'
|
||||
testCompile 'com.icegreen:greenmail:1.5.11'
|
||||
}
|
||||
|
||||
jar {
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
||||
#Thu Jul 04 22:47:59 MSK 2019
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
|
@@ -20,7 +20,12 @@
|
||||
|
||||
package io.kamax.mxisd;
|
||||
|
||||
import io.kamax.mxisd.config.MatrixConfig;
|
||||
import io.kamax.mxisd.config.MxisdConfig;
|
||||
import io.kamax.mxisd.config.PolicyConfig;
|
||||
import io.kamax.mxisd.http.undertow.handler.ApiHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.AuthorizationHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.CheckTermsHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.InternalInfoHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.OptionsHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.SaneHandler;
|
||||
@@ -31,19 +36,46 @@ 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;
|
||||
import io.kamax.mxisd.http.undertow.handler.identity.share.KeyGetHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.identity.share.RegularKeyIsValidHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.identity.share.SessionStartHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.identity.share.SessionTpidBindHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.identity.share.SessionTpidGetValidatedHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.identity.share.SessionTpidUnbindHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.identity.share.SessionValidationGetHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.identity.share.SessionValidationPostHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.identity.share.SignEd25519Handler;
|
||||
import io.kamax.mxisd.http.undertow.handler.identity.share.StoreInviteHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.identity.v1.*;
|
||||
import io.kamax.mxisd.http.undertow.handler.identity.v2.HashDetailsHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.identity.v2.HashLookupHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.invite.v1.RoomInviteHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.profile.v1.InternalProfileHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.profile.v1.ProfileHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.register.v1.Register3pidRequestTokenHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.status.StatusHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.status.VersionHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.term.v2.AcceptTermsHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.term.v2.GetTermsHandler;
|
||||
import io.kamax.mxisd.matrix.IdentityServiceAPI;
|
||||
import io.undertow.Handlers;
|
||||
import io.undertow.Undertow;
|
||||
import io.undertow.server.HttpHandler;
|
||||
import io.undertow.server.RoutingHandler;
|
||||
import io.undertow.util.HttpString;
|
||||
import io.undertow.util.Methods;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class HttpMxisd {
|
||||
|
||||
@@ -66,16 +98,11 @@ public class HttpMxisd {
|
||||
public void start() {
|
||||
m.start();
|
||||
|
||||
HttpHandler helloHandler = SaneHandler.around(new HelloHandler());
|
||||
|
||||
HttpHandler asUserHandler = SaneHandler.around(new AsUserHandler(m.getAs()));
|
||||
HttpHandler asTxnHandler = SaneHandler.around(new AsTransactionHandler(m.getAs()));
|
||||
HttpHandler asNotFoundHandler = SaneHandler.around(new AsNotFoundHandler(m.getAs()));
|
||||
|
||||
HttpHandler storeInvHandler = SaneHandler.around(new StoreInviteHandler(m.getConfig().getServer(), m.getInvite(), m.getKeyManager()));
|
||||
|
||||
httpSrv = Undertow.builder().addHttpListener(m.getConfig().getServer().getPort(), "0.0.0.0").setHandler(Handlers.routing()
|
||||
|
||||
final RoutingHandler handler = Handlers.routing()
|
||||
.add("OPTIONS", "/**", SaneHandler.around(new OptionsHandler()))
|
||||
|
||||
// Status endpoints
|
||||
@@ -87,34 +114,23 @@ 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(AuthorizationHandler.around(m.getAccMgr(), new AccountGetUserInfoHandler(m.getAccMgr()))))
|
||||
.post(AccountLogoutHandler.Path,
|
||||
SaneHandler.around(AuthorizationHandler.around(m.getAccMgr(), new AccountLogoutHandler(m.getAccMgr()))))
|
||||
|
||||
// Directory endpoints
|
||||
.post(UserDirectorySearchHandler.Path, SaneHandler.around(new UserDirectorySearchHandler(m.getDirectory())))
|
||||
|
||||
// Key endpoints
|
||||
.get(KeyGetHandler.Path, SaneHandler.around(new KeyGetHandler(m.getKeyManager())))
|
||||
.get(RegularKeyIsValidHandler.Path, SaneHandler.around(new RegularKeyIsValidHandler(m.getKeyManager())))
|
||||
.get(EphemeralKeyIsValidHandler.Path, SaneHandler.around(new EphemeralKeyIsValidHandler(m.getKeyManager())))
|
||||
|
||||
// Identity endpoints
|
||||
.get(HelloHandler.Path, helloHandler)
|
||||
.get(HelloHandler.Path + "/", helloHandler) // Be lax with possibly trailing slash
|
||||
.get(SingleLookupHandler.Path, SaneHandler.around(new SingleLookupHandler(m.getConfig(), m.getIdentity(), m.getSign())))
|
||||
.post(BulkLookupHandler.Path, SaneHandler.around(new BulkLookupHandler(m.getIdentity())))
|
||||
.post(StoreInviteHandler.Path, storeInvHandler)
|
||||
.post(SessionStartHandler.Path, SaneHandler.around(new SessionStartHandler(m.getSession())))
|
||||
.get(SessionValidateHandler.Path, SaneHandler.around(new SessionValidationGetHandler(m.getSession(), m.getConfig())))
|
||||
.post(SessionValidateHandler.Path, SaneHandler.around(new SessionValidationPostHandler(m.getSession())))
|
||||
.get(SessionTpidGetValidatedHandler.Path, SaneHandler.around(new SessionTpidGetValidatedHandler(m.getSession())))
|
||||
.post(SessionTpidBindHandler.Path, SaneHandler.around(new SessionTpidBindHandler(m.getSession(), m.getInvite(), m.getSign())))
|
||||
.post(SessionTpidUnbindHandler.Path, SaneHandler.around(new SessionTpidUnbindHandler(m.getSession())))
|
||||
.post(SignEd25519Handler.Path, SaneHandler.around(new SignEd25519Handler(m.getConfig(), m.getInvite(), m.getSign())))
|
||||
|
||||
// Profile endpoints
|
||||
.get(ProfileHandler.Path, SaneHandler.around(new ProfileHandler(m.getProfile())))
|
||||
.get(InternalProfileHandler.Path, SaneHandler.around(new InternalProfileHandler(m.getProfile())))
|
||||
|
||||
// Registration endpoints
|
||||
.post(Register3pidRequestTokenHandler.Path, SaneHandler.around(new Register3pidRequestTokenHandler(m.getReg(), m.getClientDns(), m.getHttpClient())))
|
||||
.post(Register3pidRequestTokenHandler.Path,
|
||||
SaneHandler.around(new Register3pidRequestTokenHandler(m.getReg(), m.getClientDns(), m.getHttpClient())))
|
||||
|
||||
// Invite endpoints
|
||||
.post(RoomInviteHandler.Path, SaneHandler.around(new RoomInviteHandler(m.getHttpClient(), m.getClientDns(), m.getInvite())))
|
||||
@@ -129,18 +145,103 @@ public class HttpMxisd {
|
||||
.put("/transactions/{" + AsTransactionHandler.ID + "}", asTxnHandler) // Legacy endpoint
|
||||
|
||||
// Banned endpoints
|
||||
.get(InternalInfoHandler.Path, SaneHandler.around(new InternalInfoHandler()))
|
||||
|
||||
).build();
|
||||
.get(InternalInfoHandler.Path, SaneHandler.around(new InternalInfoHandler()));
|
||||
keyEndpoints(handler);
|
||||
identityEndpoints(handler);
|
||||
termsEndpoints(handler);
|
||||
hashEndpoints(handler);
|
||||
httpSrv = Undertow.builder().addHttpListener(m.getConfig().getServer().getPort(), "0.0.0.0").setHandler(handler).build();
|
||||
|
||||
httpSrv.start();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
// Because it might have never been initialized if an exception is thrown early
|
||||
if (Objects.nonNull(httpSrv)) httpSrv.stop();
|
||||
if (Objects.nonNull(httpSrv)) {
|
||||
httpSrv.stop();
|
||||
}
|
||||
|
||||
m.stop();
|
||||
}
|
||||
|
||||
private void keyEndpoints(RoutingHandler routingHandler) {
|
||||
addEndpoints(routingHandler, Methods.GET, false,
|
||||
new KeyGetHandler(m.getKeyManager()),
|
||||
new RegularKeyIsValidHandler(m.getKeyManager()),
|
||||
new EphemeralKeyIsValidHandler(m.getKeyManager())
|
||||
);
|
||||
}
|
||||
|
||||
private void identityEndpoints(RoutingHandler routingHandler) {
|
||||
// Legacy v1
|
||||
routingHandler.get(SingleLookupHandler.Path, sane(new SingleLookupHandler(m.getConfig(), m.getIdentity(), m.getSign())));
|
||||
routingHandler.post(BulkLookupHandler.Path, sane(new BulkLookupHandler(m.getIdentity())));
|
||||
|
||||
addEndpoints(routingHandler, Methods.GET, false, new HelloHandler());
|
||||
|
||||
addEndpoints(routingHandler, Methods.GET, true,
|
||||
new SessionValidationGetHandler(m.getSession(), m.getConfig()),
|
||||
new SessionTpidGetValidatedHandler(m.getSession())
|
||||
);
|
||||
addEndpoints(routingHandler, Methods.POST, true,
|
||||
new StoreInviteHandler(m.getConfig().getServer(), m.getInvite(), m.getKeyManager()),
|
||||
new SessionStartHandler(m.getSession()),
|
||||
new SessionValidationPostHandler(m.getSession()),
|
||||
new SessionTpidBindHandler(m.getSession(), m.getInvite(), m.getSign()),
|
||||
new SessionTpidUnbindHandler(m.getSession()),
|
||||
new SignEd25519Handler(m.getConfig(), m.getInvite(), m.getSign())
|
||||
);
|
||||
}
|
||||
|
||||
private void termsEndpoints(RoutingHandler routingHandler) {
|
||||
routingHandler.get(GetTermsHandler.PATH, new GetTermsHandler(m.getConfig().getPolicy()));
|
||||
routingHandler
|
||||
.post(AcceptTermsHandler.PATH, AuthorizationHandler.around(m.getAccMgr(), sane(new AcceptTermsHandler(m.getAccMgr()))));
|
||||
}
|
||||
|
||||
private void hashEndpoints(RoutingHandler routingHandler) {
|
||||
routingHandler
|
||||
.get(HashDetailsHandler.PATH, AuthorizationHandler.around(m.getAccMgr(), sane(new HashDetailsHandler(m.getHashManager()))));
|
||||
routingHandler.post(HashLookupHandler.Path,
|
||||
AuthorizationHandler.around(m.getAccMgr(), sane(new HashLookupHandler(m.getIdentity(), m.getHashManager()))));
|
||||
}
|
||||
|
||||
private void addEndpoints(RoutingHandler routingHandler, HttpString method, boolean useAuthorization, ApiHandler... handlers) {
|
||||
for (ApiHandler handler : handlers) {
|
||||
attachHandler(routingHandler, method, handler, useAuthorization, sane(handler));
|
||||
}
|
||||
}
|
||||
|
||||
private void attachHandler(RoutingHandler routingHandler, HttpString method, ApiHandler apiHandler, boolean useAuthorization,
|
||||
HttpHandler httpHandler) {
|
||||
MatrixConfig matrixConfig = m.getConfig().getMatrix();
|
||||
if (matrixConfig.isV1()) {
|
||||
routingHandler.add(method, apiHandler.getPath(IdentityServiceAPI.V1), httpHandler);
|
||||
}
|
||||
if (matrixConfig.isV2()) {
|
||||
HttpHandler handlerWithTerms = CheckTermsHandler.around(m.getAccMgr(), httpHandler, getPolicyObjects(apiHandler));
|
||||
HttpHandler wrappedHandler = useAuthorization ? AuthorizationHandler.around(m.getAccMgr(), handlerWithTerms) : handlerWithTerms;
|
||||
routingHandler.add(method, apiHandler.getPath(IdentityServiceAPI.V2), wrappedHandler);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<PolicyConfig.PolicyObject> getPolicyObjects(ApiHandler apiHandler) {
|
||||
PolicyConfig policyConfig = m.getConfig().getPolicy();
|
||||
List<PolicyConfig.PolicyObject> policies = new ArrayList<>();
|
||||
if (!policyConfig.getPolicies().isEmpty()) {
|
||||
for (PolicyConfig.PolicyObject policy : policyConfig.getPolicies().values()) {
|
||||
for (Pattern pattern : policy.getPatterns()) {
|
||||
if (pattern.matcher(apiHandler.getHandlerPath()).matches()) {
|
||||
policies.add(policy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return policies;
|
||||
}
|
||||
|
||||
private HttpHandler sane(HttpHandler httpHandler) {
|
||||
return SaneHandler.around(httpHandler);
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
@@ -34,6 +35,7 @@ import io.kamax.mxisd.directory.DirectoryManager;
|
||||
import io.kamax.mxisd.directory.DirectoryProviders;
|
||||
import io.kamax.mxisd.dns.ClientDnsOverwrite;
|
||||
import io.kamax.mxisd.dns.FederationDnsOverwrite;
|
||||
import io.kamax.mxisd.hash.HashManager;
|
||||
import io.kamax.mxisd.invitation.InvitationManager;
|
||||
import io.kamax.mxisd.lookup.ThreePidProviders;
|
||||
import io.kamax.mxisd.lookup.fetcher.IRemoteIdentityServerFetcher;
|
||||
@@ -85,6 +87,8 @@ public class Mxisd {
|
||||
private SessionManager sessMgr;
|
||||
private NotificationManager notifMgr;
|
||||
private RegistrationManager regMgr;
|
||||
private AccountManager accMgr;
|
||||
private HashManager hashManager;
|
||||
|
||||
// HS-specific classes
|
||||
private Synapse synapse;
|
||||
@@ -115,7 +119,10 @@ public class Mxisd {
|
||||
ServiceLoader.load(IdentityStoreSupplier.class).iterator().forEachRemaining(p -> p.accept(this));
|
||||
ServiceLoader.load(NotificationHandlerSupplier.class).iterator().forEachRemaining(p -> p.accept(this));
|
||||
|
||||
idStrategy = new RecursivePriorityLookupStrategy(cfg.getLookup(), ThreePidProviders.get(), bridgeFetcher);
|
||||
hashManager = new HashManager();
|
||||
hashManager.init(cfg.getHashing(), ThreePidProviders.get(), store);
|
||||
|
||||
idStrategy = new RecursivePriorityLookupStrategy(cfg.getLookup(), ThreePidProviders.get(), bridgeFetcher, hashManager);
|
||||
pMgr = new ProfileManager(ProfileProviders.get(), clientDns, httpClient);
|
||||
notifMgr = new NotificationManager(cfg.getNotification(), NotificationHandlers.get());
|
||||
sessMgr = new SessionManager(cfg, store, notifMgr, resolver, httpClient, signMgr);
|
||||
@@ -124,6 +131,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 +202,14 @@ public class Mxisd {
|
||||
return synapse;
|
||||
}
|
||||
|
||||
public AccountManager getAccMgr() {
|
||||
return accMgr;
|
||||
}
|
||||
|
||||
public HashManager getHashManager() {
|
||||
return hashManager;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
build();
|
||||
}
|
||||
|
160
src/main/java/io/kamax/mxisd/auth/AccountManager.java
Normal file
160
src/main/java/io/kamax/mxisd/auth/AccountManager.java
Normal file
@@ -0,0 +1,160 @@
|
||||
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.config.PolicyConfig;
|
||||
import io.kamax.mxisd.exception.BadRequestException;
|
||||
import io.kamax.mxisd.exception.InvalidCredentialsException;
|
||||
import io.kamax.mxisd.exception.NotFoundException;
|
||||
import io.kamax.mxisd.http.undertow.handler.auth.v1.LoginGetHandler;
|
||||
import io.kamax.mxisd.matrix.HomeserverFederationResolver;
|
||||
import io.kamax.mxisd.storage.IStorage;
|
||||
import io.kamax.mxisd.storage.ormlite.dao.AccountDao;
|
||||
import org.apache.http.HttpStatus;
|
||||
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 org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public class AccountManager {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AccountManager.class);
|
||||
|
||||
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, AccountConfig accountConfig, MatrixConfig matrixConfig) {
|
||||
this.storage = storage;
|
||||
this.resolver = resolver;
|
||||
this.httpClient = httpClient;
|
||||
this.accountConfig = accountConfig;
|
||||
this.matrixConfig = matrixConfig;
|
||||
}
|
||||
|
||||
public String register(OpenIdToken openIdToken) {
|
||||
Objects.requireNonNull(openIdToken.getAccessToken(), "Missing required access_token");
|
||||
Objects.requireNonNull(openIdToken.getTokenType(), "Missing required token type");
|
||||
Objects.requireNonNull(openIdToken.getMatrixServerName(), "Missing required matrix domain");
|
||||
|
||||
LOGGER.info("Registration from the server: {}", openIdToken.getMatrixServerName());
|
||||
String userId = getUserId(openIdToken);
|
||||
LOGGER.info("UserId: {}", userId);
|
||||
|
||||
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();
|
||||
LOGGER.info("Domain resolved: {} => {}", openIdToken.getMatrixServerName(), homeserverURL);
|
||||
HttpGet getUserInfo = new HttpGet(
|
||||
homeserverURL + "/_matrix/federation/v1/openid/userinfo?access_token=" + openIdToken.getAccessToken());
|
||||
String userId;
|
||||
try (CloseableHttpResponse response = httpClient.execute(getUserInfo)) {
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
if (statusCode == HttpStatus.SC_OK) {
|
||||
String content = EntityUtils.toString(response.getEntity());
|
||||
LOGGER.trace("Response: {}", content);
|
||||
JsonObject body = GsonUtil.parseObj(content);
|
||||
userId = GsonUtil.getStringOrThrow(body, "sub");
|
||||
} else {
|
||||
LOGGER.error("Wrong response status: {}", statusCode);
|
||||
throw new InvalidCredentialsException();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Unable to get user info.", e);
|
||||
throw new InvalidCredentialsException();
|
||||
}
|
||||
|
||||
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) {
|
||||
return storage.findAccount(token).orElseThrow(NotFoundException::new).getUserId();
|
||||
}
|
||||
|
||||
public AccountDao findAccount(String token) {
|
||||
AccountDao accountDao = storage.findAccount(token).orElse(null);
|
||||
|
||||
if (LOGGER.isInfoEnabled()) {
|
||||
if (accountDao != null) {
|
||||
LOGGER.info("Found account for user: {}", accountDao.getUserId());
|
||||
} else {
|
||||
LOGGER.warn("Account not found.");
|
||||
}
|
||||
}
|
||||
return accountDao;
|
||||
}
|
||||
|
||||
public void logout(String token) {
|
||||
String userId = storage.findAccount(token).orElseThrow(InvalidCredentialsException::new).getUserId();
|
||||
LOGGER.info("Logout: {}", userId);
|
||||
deleteAccount(token);
|
||||
}
|
||||
|
||||
public void deleteAccount(String token) {
|
||||
storage.deleteAccepts(token);
|
||||
storage.deleteToken(token);
|
||||
}
|
||||
|
||||
public void acceptTerm(String token, String url) {
|
||||
storage.acceptTerm(token, url);
|
||||
}
|
||||
|
||||
public boolean isTermAccepted(String token, List<PolicyConfig.PolicyObject> policies) {
|
||||
return policies.isEmpty() || storage.isTermAccepted(token, policies);
|
||||
}
|
||||
|
||||
public AccountConfig getAccountConfig() {
|
||||
return accountConfig;
|
||||
}
|
||||
|
||||
public MatrixConfig getMatrixConfig() {
|
||||
return matrixConfig;
|
||||
}
|
||||
}
|
44
src/main/java/io/kamax/mxisd/auth/OpenIdToken.java
Normal file
44
src/main/java/io/kamax/mxisd/auth/OpenIdToken.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package io.kamax.mxisd.auth;
|
||||
|
||||
public class OpenIdToken {
|
||||
|
||||
private String accessToken;
|
||||
|
||||
private String tokenType;
|
||||
|
||||
private String matrixServerName;
|
||||
|
||||
private long expiredIn;
|
||||
|
||||
public String getAccessToken() {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
public void setAccessToken(String accessToken) {
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
|
||||
public String getTokenType() {
|
||||
return tokenType;
|
||||
}
|
||||
|
||||
public void setTokenType(String tokenType) {
|
||||
this.tokenType = tokenType;
|
||||
}
|
||||
|
||||
public String getMatrixServerName() {
|
||||
return matrixServerName;
|
||||
}
|
||||
|
||||
public void setMatrixServerName(String matrixServerName) {
|
||||
this.matrixServerName = matrixServerName;
|
||||
}
|
||||
|
||||
public long getExpiredIn() {
|
||||
return expiredIn;
|
||||
}
|
||||
|
||||
public void setExpiredIn(long expiredIn) {
|
||||
this.expiredIn = expiredIn;
|
||||
}
|
||||
}
|
@@ -164,6 +164,26 @@ public class ExecIdentityStore extends ExecStore implements IThreePidProvider {
|
||||
return input.toString();
|
||||
});
|
||||
|
||||
addBulkSuccessMapper(p);
|
||||
|
||||
p.withFailureDefault(output -> Collections.emptyList());
|
||||
|
||||
return p.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<ThreePidMapping> populateHashes() {
|
||||
Processor<List<ThreePidMapping>> p = new Processor<>();
|
||||
p.withConfig(cfg.getLookup().getBulk());
|
||||
|
||||
addBulkSuccessMapper(p);
|
||||
|
||||
p.withFailureDefault(output -> Collections.emptyList());
|
||||
|
||||
return p.execute();
|
||||
}
|
||||
|
||||
private void addBulkSuccessMapper(Processor<List<ThreePidMapping>> p) {
|
||||
p.addSuccessMapper(JsonType, output -> {
|
||||
if (StringUtils.isBlank(output)) {
|
||||
return Collections.emptyList();
|
||||
@@ -188,10 +208,5 @@ public class ExecIdentityStore extends ExecStore implements IThreePidProvider {
|
||||
throw new InternalServerError("Invalid user type: " + item.getId().getType());
|
||||
}).collect(Collectors.toList());
|
||||
});
|
||||
|
||||
p.withFailureDefault(output -> Collections.emptyList());
|
||||
|
||||
return p.execute();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -48,6 +48,7 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class MemoryIdentityStore implements AuthenticatorProvider, DirectoryProvider, IThreePidProvider, ProfileProvider {
|
||||
|
||||
@@ -171,4 +172,11 @@ public class MemoryIdentityStore implements AuthenticatorProvider, DirectoryProv
|
||||
}).orElseGet(BackendAuthResult::failure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<ThreePidMapping> populateHashes() {
|
||||
return cfg.getIdentities().stream()
|
||||
.map(mic -> mic.getThreepids().stream().map(mtp -> new ThreePidMapping(mtp.getMedium(), mtp.getAddress(), mic.getUsername())))
|
||||
.flatMap(s -> s).collect(
|
||||
Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
@@ -36,6 +36,7 @@ import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -104,4 +105,27 @@ public abstract class SqlThreePidProvider implements IThreePidProvider {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<ThreePidMapping> populateHashes() {
|
||||
if (StringUtils.isBlank(cfg.getLookup().getQuery())) {
|
||||
log.warn("Lookup query not configured, skip.");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<ThreePidMapping> result = new ArrayList<>();
|
||||
try (Connection connection = pool.get()) {
|
||||
PreparedStatement statement = connection.prepareStatement(cfg.getLookup().getQuery());
|
||||
try (ResultSet resultSet = statement.executeQuery()) {
|
||||
while (resultSet.next()) {
|
||||
String mxid = resultSet.getString("mxid");
|
||||
String medium = resultSet.getString("medium");
|
||||
String address = resultSet.getString("address");
|
||||
result.add(new ThreePidMapping(medium, address, mxid));
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
8
src/main/java/io/kamax/mxisd/config/AcceptingPolicy.java
Normal file
8
src/main/java/io/kamax/mxisd/config/AcceptingPolicy.java
Normal file
@@ -0,0 +1,8 @@
|
||||
package io.kamax.mxisd.config;
|
||||
|
||||
public enum AcceptingPolicy {
|
||||
|
||||
ALL,
|
||||
|
||||
ANY
|
||||
}
|
24
src/main/java/io/kamax/mxisd/config/AccountConfig.java
Normal file
24
src/main/java/io/kamax/mxisd/config/AccountConfig.java
Normal 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());
|
||||
}
|
||||
}
|
96
src/main/java/io/kamax/mxisd/config/HashingConfig.java
Normal file
96
src/main/java/io/kamax/mxisd/config/HashingConfig.java
Normal file
@@ -0,0 +1,96 @@
|
||||
package io.kamax.mxisd.config;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class HashingConfig {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HashingConfig.class);
|
||||
|
||||
private boolean enabled = false;
|
||||
private int pepperLength = 10;
|
||||
private RotationPolicyEnum rotationPolicy;
|
||||
private HashStorageEnum hashStorageType;
|
||||
private long delay = 10;
|
||||
private List<Algorithm> algorithms = new ArrayList<>();
|
||||
|
||||
public void build() {
|
||||
if (isEnabled()) {
|
||||
LOGGER.info("--- Hash configuration ---");
|
||||
LOGGER.info(" Pepper length: {}", getPepperLength());
|
||||
LOGGER.info(" Rotation policy: {}", getRotationPolicy());
|
||||
LOGGER.info(" Hash storage type: {}", getHashStorageType());
|
||||
if (RotationPolicyEnum.PER_SECONDS == rotationPolicy) {
|
||||
LOGGER.info(" Rotation delay: {}", delay);
|
||||
}
|
||||
} else {
|
||||
LOGGER.info("Hash configuration disabled, used only `none` pepper.");
|
||||
}
|
||||
}
|
||||
|
||||
public enum Algorithm {
|
||||
NONE,
|
||||
SHA256
|
||||
}
|
||||
|
||||
public enum RotationPolicyEnum {
|
||||
PER_REQUESTS,
|
||||
PER_SECONDS
|
||||
}
|
||||
|
||||
public enum HashStorageEnum {
|
||||
IN_MEMORY,
|
||||
SQL
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public int getPepperLength() {
|
||||
return pepperLength;
|
||||
}
|
||||
|
||||
public void setPepperLength(int pepperLength) {
|
||||
this.pepperLength = pepperLength;
|
||||
}
|
||||
|
||||
public RotationPolicyEnum getRotationPolicy() {
|
||||
return rotationPolicy;
|
||||
}
|
||||
|
||||
public void setRotationPolicy(RotationPolicyEnum rotationPolicy) {
|
||||
this.rotationPolicy = rotationPolicy;
|
||||
}
|
||||
|
||||
public HashStorageEnum getHashStorageType() {
|
||||
return hashStorageType;
|
||||
}
|
||||
|
||||
public void setHashStorageType(HashStorageEnum hashStorageType) {
|
||||
this.hashStorageType = hashStorageType;
|
||||
}
|
||||
|
||||
public long getDelay() {
|
||||
return delay;
|
||||
}
|
||||
|
||||
public void setDelay(long delay) {
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
public List<Algorithm> getAlgorithms() {
|
||||
return algorithms;
|
||||
}
|
||||
|
||||
public void setAlgorithms(List<Algorithm> algorithms) {
|
||||
this.algorithms = algorithms;
|
||||
}
|
||||
}
|
@@ -63,6 +63,8 @@ public class MatrixConfig {
|
||||
|
||||
private String domain;
|
||||
private Identity identity = new Identity();
|
||||
private boolean v1 = true;
|
||||
private boolean v2 = true;
|
||||
|
||||
public String getDomain() {
|
||||
return domain;
|
||||
@@ -80,6 +82,22 @@ public class MatrixConfig {
|
||||
this.identity = identity;
|
||||
}
|
||||
|
||||
public boolean isV1() {
|
||||
return v1;
|
||||
}
|
||||
|
||||
public void setV1(boolean v1) {
|
||||
this.v1 = v1;
|
||||
}
|
||||
|
||||
public boolean isV2() {
|
||||
return v2;
|
||||
}
|
||||
|
||||
public void setV2(boolean v2) {
|
||||
this.v2 = v2;
|
||||
}
|
||||
|
||||
public void build() {
|
||||
log.info("--- Matrix config ---");
|
||||
|
||||
@@ -90,6 +108,11 @@ public class MatrixConfig {
|
||||
log.info("Domain: {}", getDomain());
|
||||
log.info("Identity:");
|
||||
log.info("\tServers: {}", GsonUtil.get().toJson(identity.getServers()));
|
||||
log.info("API v1: {}", v1);
|
||||
log.info("API v2: {}", v2);
|
||||
if (v1) {
|
||||
log.warn("API v1 is deprecated via MSC2140: https://github.com/matrix-org/matrix-doc/pull/2140 and will be deleted in future releases.");
|
||||
log.warn("Please upgrade your homeserver and enable only API v2.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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();
|
||||
@@ -114,6 +115,8 @@ public class MxisdConfig {
|
||||
private ThreePidConfig threepid = new ThreePidConfig();
|
||||
private ViewConfig view = new ViewConfig();
|
||||
private WordpressConfig wordpress = new WordpressConfig();
|
||||
private PolicyConfig policy = new PolicyConfig();
|
||||
private HashingConfig hashing = new HashingConfig();
|
||||
|
||||
public AppServiceConfig getAppsvc() {
|
||||
return appsvc;
|
||||
@@ -131,6 +134,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;
|
||||
}
|
||||
@@ -315,6 +326,22 @@ public class MxisdConfig {
|
||||
this.wordpress = wordpress;
|
||||
}
|
||||
|
||||
public PolicyConfig getPolicy() {
|
||||
return policy;
|
||||
}
|
||||
|
||||
public void setPolicy(PolicyConfig policy) {
|
||||
this.policy = policy;
|
||||
}
|
||||
|
||||
public HashingConfig getHashing() {
|
||||
return hashing;
|
||||
}
|
||||
|
||||
public void setHashing(HashingConfig hashing) {
|
||||
this.hashing = hashing;
|
||||
}
|
||||
|
||||
public MxisdConfig inMemory() {
|
||||
getKey().setPath(":memory:");
|
||||
getStorage().getProvider().getSqlite().setDatabase(":memory:");
|
||||
@@ -330,6 +357,7 @@ public class MxisdConfig {
|
||||
|
||||
getAppsvc().build();
|
||||
getAuth().build();
|
||||
getAccountConfig().build();
|
||||
getDirectory().build();
|
||||
getExec().build();
|
||||
getFirebase().build();
|
||||
@@ -352,6 +380,8 @@ public class MxisdConfig {
|
||||
getThreepid().build();
|
||||
getView().build();
|
||||
getWordpress().build();
|
||||
getPolicy().build();
|
||||
getHashing().build();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
112
src/main/java/io/kamax/mxisd/config/PolicyConfig.java
Normal file
112
src/main/java/io/kamax/mxisd/config/PolicyConfig.java
Normal file
@@ -0,0 +1,112 @@
|
||||
package io.kamax.mxisd.config;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class PolicyConfig {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(PolicyConfig.class);
|
||||
|
||||
private Map<String, PolicyObject> policies = new HashMap<>();
|
||||
|
||||
public static class TermObject {
|
||||
|
||||
private String name;
|
||||
|
||||
private String url;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PolicyObject {
|
||||
|
||||
private String version;
|
||||
|
||||
private Map<String, TermObject> terms;
|
||||
|
||||
private List<String> regexp = new ArrayList<>();
|
||||
|
||||
private transient List<Pattern> patterns = new ArrayList<>();
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public Map<String, TermObject> getTerms() {
|
||||
return terms;
|
||||
}
|
||||
|
||||
public void setTerms(Map<String, TermObject> terms) {
|
||||
this.terms = terms;
|
||||
}
|
||||
|
||||
public List<String> getRegexp() {
|
||||
return regexp;
|
||||
}
|
||||
|
||||
public void setRegexp(List<String> regexp) {
|
||||
this.regexp = regexp;
|
||||
}
|
||||
|
||||
public List<Pattern> getPatterns() {
|
||||
return patterns;
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, PolicyObject> getPolicies() {
|
||||
return policies;
|
||||
}
|
||||
|
||||
public void setPolicies(Map<String, PolicyObject> policies) {
|
||||
this.policies = policies;
|
||||
}
|
||||
|
||||
public void build() {
|
||||
LOGGER.info("--- Policy Config ---");
|
||||
if (getPolicies().isEmpty()) {
|
||||
LOGGER.info("Empty");
|
||||
} else {
|
||||
for (Map.Entry<String, PolicyObject> policyObjectItem : getPolicies().entrySet()) {
|
||||
PolicyObject policyObject = policyObjectItem.getValue();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Policy \"").append(policyObjectItem.getKey()).append("\"\n");
|
||||
sb.append(" version: ").append(policyObject.getVersion()).append("\n");
|
||||
for (String regexp : policyObjectItem.getValue().getRegexp()) {
|
||||
sb.append(" - ").append(regexp).append("\n");
|
||||
policyObjectItem.getValue().getPatterns().add(Pattern.compile(regexp));
|
||||
}
|
||||
sb.append(" terms:\n");
|
||||
for (Map.Entry<String, TermObject> termItem : policyObject.getTerms().entrySet()) {
|
||||
sb.append(" - lang: ").append(termItem.getKey()).append("\n");
|
||||
sb.append(" name: ").append(termItem.getValue().getName()).append("\n");
|
||||
sb.append(" url: ").append(termItem.getValue().getUrl()).append("\n");
|
||||
}
|
||||
LOGGER.info(sb.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -79,5 +79,4 @@ public class ServerConfig {
|
||||
log.info("Port: {}", getPort());
|
||||
log.info("Public URL: {}", getPublicUrl());
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -124,6 +124,18 @@ public abstract class SqlConfig {
|
||||
|
||||
}
|
||||
|
||||
public static class Lookup {
|
||||
private String query;
|
||||
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public void setQuery(String query) {
|
||||
this.query = query;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Identity {
|
||||
|
||||
private Boolean enabled;
|
||||
@@ -264,6 +276,7 @@ public abstract class SqlConfig {
|
||||
private Directory directory = new Directory();
|
||||
private Identity identity = new Identity();
|
||||
private Profile profile = new Profile();
|
||||
private Lookup lookup = new Lookup();
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
@@ -321,6 +334,14 @@ public abstract class SqlConfig {
|
||||
this.profile = profile;
|
||||
}
|
||||
|
||||
public Lookup getLookup() {
|
||||
return lookup;
|
||||
}
|
||||
|
||||
public void setLookup(Lookup lookup) {
|
||||
this.lookup = lookup;
|
||||
}
|
||||
|
||||
protected abstract String getProviderName();
|
||||
|
||||
public void build() {
|
||||
@@ -354,6 +375,7 @@ public abstract class SqlConfig {
|
||||
log.info("Identity type: {}", getIdentity().getType());
|
||||
log.info("3PID mapping query: {}", getIdentity().getQuery());
|
||||
log.info("Identity medium queries: {}", GsonUtil.build().toJson(getIdentity().getMedium()));
|
||||
log.info("Lookup query: {}", getLookup().getQuery());
|
||||
log.info("Profile:");
|
||||
log.info(" Enabled: {}", getProfile().isEnabled());
|
||||
if (getProfile().isEnabled()) {
|
||||
|
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.exception;
|
||||
|
||||
public class InvalidParamException extends RuntimeException {
|
||||
|
||||
public InvalidParamException() {
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.exception;
|
||||
|
||||
public class InvalidPepperException extends RuntimeException {
|
||||
|
||||
public InvalidPepperException() {
|
||||
}
|
||||
}
|
51
src/main/java/io/kamax/mxisd/hash/HashEngine.java
Normal file
51
src/main/java/io/kamax/mxisd/hash/HashEngine.java
Normal file
@@ -0,0 +1,51 @@
|
||||
package io.kamax.mxisd.hash;
|
||||
|
||||
import io.kamax.matrix.codec.MxSha256;
|
||||
import io.kamax.mxisd.config.HashingConfig;
|
||||
import io.kamax.mxisd.hash.storage.HashStorage;
|
||||
import io.kamax.mxisd.lookup.ThreePidMapping;
|
||||
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class HashEngine {
|
||||
|
||||
private final List<? extends IThreePidProvider> providers;
|
||||
private final HashStorage hashStorage;
|
||||
private final MxSha256 sha256 = new MxSha256();
|
||||
private final HashingConfig config;
|
||||
private String pepper;
|
||||
|
||||
public HashEngine(List<? extends IThreePidProvider> providers, HashStorage hashStorage, HashingConfig config) {
|
||||
this.providers = providers;
|
||||
this.hashStorage = hashStorage;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public void updateHashes() {
|
||||
synchronized (hashStorage) {
|
||||
this.pepper = newPepper();
|
||||
hashStorage.clear();
|
||||
for (IThreePidProvider provider : providers) {
|
||||
for (ThreePidMapping pidMapping : provider.populateHashes()) {
|
||||
hashStorage.add(pidMapping, hash(pidMapping));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getPepper() {
|
||||
synchronized (hashStorage) {
|
||||
return pepper;
|
||||
}
|
||||
}
|
||||
|
||||
protected String hash(ThreePidMapping pidMapping) {
|
||||
return sha256.hash(pidMapping.getMedium() + " " + pidMapping.getValue() + " " + getPepper());
|
||||
}
|
||||
|
||||
protected String newPepper() {
|
||||
return RandomStringUtils.random(config.getPepperLength());
|
||||
}
|
||||
}
|
91
src/main/java/io/kamax/mxisd/hash/HashManager.java
Normal file
91
src/main/java/io/kamax/mxisd/hash/HashManager.java
Normal file
@@ -0,0 +1,91 @@
|
||||
package io.kamax.mxisd.hash;
|
||||
|
||||
import io.kamax.mxisd.config.HashingConfig;
|
||||
import io.kamax.mxisd.hash.rotation.HashRotationStrategy;
|
||||
import io.kamax.mxisd.hash.rotation.NoOpRotationStrategy;
|
||||
import io.kamax.mxisd.hash.rotation.RotationPerRequests;
|
||||
import io.kamax.mxisd.hash.rotation.TimeBasedRotation;
|
||||
import io.kamax.mxisd.hash.storage.EmptyStorage;
|
||||
import io.kamax.mxisd.hash.storage.HashStorage;
|
||||
import io.kamax.mxisd.hash.storage.InMemoryHashStorage;
|
||||
import io.kamax.mxisd.hash.storage.SqlHashStorage;
|
||||
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
|
||||
import io.kamax.mxisd.storage.IStorage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class HashManager {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HashManager.class);
|
||||
|
||||
private HashEngine hashEngine;
|
||||
private HashRotationStrategy rotationStrategy;
|
||||
private HashStorage hashStorage;
|
||||
private HashingConfig config;
|
||||
private IStorage storage;
|
||||
private AtomicBoolean configured = new AtomicBoolean(false);
|
||||
|
||||
public void init(HashingConfig config, List<? extends IThreePidProvider> providers, IStorage storage) {
|
||||
this.config = config;
|
||||
this.storage = storage;
|
||||
initStorage();
|
||||
hashEngine = new HashEngine(providers, getHashStorage(), config);
|
||||
initRotationStrategy();
|
||||
configured.set(true);
|
||||
}
|
||||
|
||||
private void initStorage() {
|
||||
if (config.isEnabled()) {
|
||||
switch (config.getHashStorageType()) {
|
||||
case IN_MEMORY:
|
||||
this.hashStorage = new InMemoryHashStorage();
|
||||
break;
|
||||
case SQL:
|
||||
this.hashStorage = new SqlHashStorage(storage);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown storage type: " + config.getHashStorageType());
|
||||
}
|
||||
} else {
|
||||
this.hashStorage = new EmptyStorage();
|
||||
}
|
||||
}
|
||||
|
||||
private void initRotationStrategy() {
|
||||
if (config.isEnabled()) {
|
||||
switch (config.getRotationPolicy()) {
|
||||
case PER_REQUESTS:
|
||||
this.rotationStrategy = new RotationPerRequests();
|
||||
break;
|
||||
case PER_SECONDS:
|
||||
this.rotationStrategy = new TimeBasedRotation(config.getDelay());
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown rotation type: " + config.getHashStorageType());
|
||||
}
|
||||
} else {
|
||||
this.rotationStrategy = new NoOpRotationStrategy();
|
||||
}
|
||||
|
||||
this.rotationStrategy.register(getHashEngine());
|
||||
}
|
||||
|
||||
public HashEngine getHashEngine() {
|
||||
return hashEngine;
|
||||
}
|
||||
|
||||
public HashRotationStrategy getRotationStrategy() {
|
||||
return rotationStrategy;
|
||||
}
|
||||
|
||||
public HashStorage getHashStorage() {
|
||||
return hashStorage;
|
||||
}
|
||||
|
||||
public HashingConfig getConfig() {
|
||||
return config;
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package io.kamax.mxisd.hash.rotation;
|
||||
|
||||
import io.kamax.mxisd.hash.HashEngine;
|
||||
|
||||
public interface HashRotationStrategy {
|
||||
|
||||
void register(HashEngine hashEngine);
|
||||
|
||||
HashEngine getHashEngine();
|
||||
|
||||
void newRequest();
|
||||
|
||||
default void trigger() {
|
||||
getHashEngine().updateHashes();
|
||||
}
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
package io.kamax.mxisd.hash.rotation;
|
||||
|
||||
import io.kamax.mxisd.hash.HashEngine;
|
||||
|
||||
public class NoOpRotationStrategy implements HashRotationStrategy {
|
||||
|
||||
private HashEngine hashEngine;
|
||||
|
||||
@Override
|
||||
public void register(HashEngine hashEngine) {
|
||||
this.hashEngine = hashEngine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashEngine getHashEngine() {
|
||||
return hashEngine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void newRequest() {
|
||||
// nothing to do
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
package io.kamax.mxisd.hash.rotation;
|
||||
|
||||
import io.kamax.mxisd.hash.HashEngine;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class RotationPerRequests implements HashRotationStrategy {
|
||||
|
||||
private HashEngine hashEngine;
|
||||
private final AtomicInteger counter = new AtomicInteger(0);
|
||||
|
||||
@Override
|
||||
public void register(HashEngine hashEngine) {
|
||||
this.hashEngine = hashEngine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashEngine getHashEngine() {
|
||||
return hashEngine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void newRequest() {
|
||||
int newValue = counter.incrementAndGet();
|
||||
if (newValue >= 10) {
|
||||
counter.set(0);
|
||||
trigger();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
package io.kamax.mxisd.hash.rotation;
|
||||
|
||||
import io.kamax.mxisd.hash.HashEngine;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class TimeBasedRotation implements HashRotationStrategy {
|
||||
|
||||
private final long delay;
|
||||
private HashEngine hashEngine;
|
||||
private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
|
||||
|
||||
public TimeBasedRotation(long delay) {
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(HashEngine hashEngine) {
|
||||
this.hashEngine = hashEngine;
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(executorService::shutdown));
|
||||
executorService.scheduleWithFixedDelay(this::trigger, 0, delay, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashEngine getHashEngine() {
|
||||
return hashEngine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void newRequest() {
|
||||
}
|
||||
}
|
25
src/main/java/io/kamax/mxisd/hash/storage/EmptyStorage.java
Normal file
25
src/main/java/io/kamax/mxisd/hash/storage/EmptyStorage.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package io.kamax.mxisd.hash.storage;
|
||||
|
||||
import io.kamax.mxisd.lookup.ThreePidMapping;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
public class EmptyStorage implements HashStorage {
|
||||
|
||||
@Override
|
||||
public Collection<Pair<String, ThreePidMapping>> find(Iterable<String> hashes) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(ThreePidMapping pidMapping, String hash) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
|
||||
}
|
||||
}
|
15
src/main/java/io/kamax/mxisd/hash/storage/HashStorage.java
Normal file
15
src/main/java/io/kamax/mxisd/hash/storage/HashStorage.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package io.kamax.mxisd.hash.storage;
|
||||
|
||||
import io.kamax.mxisd.lookup.ThreePidMapping;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface HashStorage {
|
||||
|
||||
Collection<Pair<String, ThreePidMapping>> find(Iterable<String> hashes);
|
||||
|
||||
void add(ThreePidMapping pidMapping, String hash);
|
||||
|
||||
void clear();
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
package io.kamax.mxisd.hash.storage;
|
||||
|
||||
import io.kamax.mxisd.lookup.ThreePidMapping;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class InMemoryHashStorage implements HashStorage {
|
||||
|
||||
private final Map<String, ThreePidMapping> mapping = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public Collection<Pair<String, ThreePidMapping>> find(Iterable<String> hashes) {
|
||||
List<Pair<String, ThreePidMapping>> result = new ArrayList<>();
|
||||
for (String hash : hashes) {
|
||||
ThreePidMapping pidMapping = mapping.get(hash);
|
||||
if (pidMapping != null) {
|
||||
result.add(Pair.of(hash, pidMapping));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(ThreePidMapping pidMapping, String hash) {
|
||||
mapping.put(hash, pidMapping);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
mapping.clear();
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
package io.kamax.mxisd.hash.storage;
|
||||
|
||||
import io.kamax.mxisd.lookup.ThreePidMapping;
|
||||
import io.kamax.mxisd.storage.IStorage;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class SqlHashStorage implements HashStorage {
|
||||
|
||||
private final IStorage storage;
|
||||
|
||||
public SqlHashStorage(IStorage storage) {
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Pair<String, ThreePidMapping>> find(Iterable<String> hashes) {
|
||||
return storage.findHashes(hashes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(ThreePidMapping pidMapping, String hash) {
|
||||
storage.addHash(pidMapping.getMxid(), pidMapping.getMedium(), pidMapping.getValue(), hash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
storage.clearHashes();
|
||||
}
|
||||
}
|
@@ -25,8 +25,6 @@ public class IsAPIv1 {
|
||||
public static final String Base = "/_matrix/identity/api/v1";
|
||||
|
||||
public static String getValidate(String medium, String sid, String secret, String token) {
|
||||
// FIXME use some kind of URLBuilder
|
||||
return Base + "/validate/" + medium + "/submitToken?sid=" + sid + "&client_secret=" + secret + "&token=" + token;
|
||||
return String.format("%s/validate/%s/submitToken?sid=%s&client_secret=%s&token=%s", Base, medium, sid, secret, token);
|
||||
}
|
||||
|
||||
}
|
||||
|
31
src/main/java/io/kamax/mxisd/http/IsAPIv2.java
Normal file
31
src/main/java/io/kamax/mxisd/http/IsAPIv2.java
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public class IsAPIv2 {
|
||||
|
||||
public static final String Base = "/_matrix/identity/v2";
|
||||
|
||||
public static String getValidate(String medium, String sid, String secret, String token) {
|
||||
return String.format("%s/validate/%s/submitToken?sid=%s&client_secret=%s&token=%s", Base, medium, sid, secret, token);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.io.identity;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ClientHashLookupAnswer {
|
||||
|
||||
private Map<String, String> mappings = new HashMap<>();
|
||||
|
||||
public Map<String, String> getMappings() {
|
||||
return mappings;
|
||||
}
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.io.identity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ClientHashLookupRequest {
|
||||
|
||||
private String algorithm;
|
||||
private String pepper;
|
||||
private List<String> addresses = new ArrayList<>();
|
||||
|
||||
public String getAlgorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
public void setAlgorithm(String algorithm) {
|
||||
this.algorithm = algorithm;
|
||||
}
|
||||
|
||||
public String getPepper() {
|
||||
return pepper;
|
||||
}
|
||||
|
||||
public void setPepper(String pepper) {
|
||||
this.pepper = pepper;
|
||||
}
|
||||
|
||||
public List<String> getAddresses() {
|
||||
return addresses;
|
||||
}
|
||||
|
||||
public void setAddresses(List<String> addresses) {
|
||||
this.addresses = addresses;
|
||||
}
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package io.kamax.mxisd.http.undertow.handler;
|
||||
|
||||
import io.kamax.mxisd.http.IsAPIv1;
|
||||
import io.kamax.mxisd.http.IsAPIv2;
|
||||
import io.kamax.mxisd.matrix.IdentityServiceAPI;
|
||||
import io.undertow.server.HttpHandler;
|
||||
|
||||
public interface ApiHandler extends HttpHandler {
|
||||
|
||||
default String getPath(IdentityServiceAPI api) {
|
||||
switch (api) {
|
||||
case V2:
|
||||
return IsAPIv2.Base + getHandlerPath();
|
||||
case V1:
|
||||
return IsAPIv1.Base + getHandlerPath();
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown api version: " + api);
|
||||
}
|
||||
}
|
||||
|
||||
String getHandlerPath();
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.auth.AccountManager;
|
||||
import io.kamax.mxisd.exception.InvalidCredentialsException;
|
||||
import io.kamax.mxisd.storage.ormlite.dao.AccountDao;
|
||||
import io.undertow.server.HttpHandler;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class AuthorizationHandler extends BasicHttpHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AuthorizationHandler.class);
|
||||
|
||||
private final AccountManager accountManager;
|
||||
|
||||
private final HttpHandler child;
|
||||
|
||||
public static AuthorizationHandler around(AccountManager accountManager, HttpHandler child) {
|
||||
return new AuthorizationHandler(accountManager, child);
|
||||
}
|
||||
|
||||
private AuthorizationHandler(AccountManager accountManager, HttpHandler child) {
|
||||
this.accountManager = accountManager;
|
||||
this.child = child;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
||||
String token = findAccessToken(exchange).orElse(null);
|
||||
if (token == null) {
|
||||
log.error("Unauthorized request from: {}", exchange.getHostAndPort());
|
||||
throw new InvalidCredentialsException();
|
||||
}
|
||||
|
||||
AccountDao account = accountManager.findAccount(token);
|
||||
if (account == null) {
|
||||
log.error("Account not found from request from: {}", exchange.getHostAndPort());
|
||||
throw new InvalidCredentialsException();
|
||||
}
|
||||
if (account.getExpiresIn() < System.currentTimeMillis()) {
|
||||
log.error("Account for '{}' from: {}", account.getUserId(), exchange.getHostAndPort());
|
||||
accountManager.deleteAccount(token);
|
||||
throw new InvalidCredentialsException();
|
||||
}
|
||||
log.trace("Access for '{}' allowed", account.getUserId());
|
||||
child.handleRequest(exchange);
|
||||
}
|
||||
}
|
@@ -28,6 +28,7 @@ import io.kamax.mxisd.exception.AccessTokenNotFoundException;
|
||||
import io.kamax.mxisd.exception.HttpMatrixException;
|
||||
import io.kamax.mxisd.exception.InternalServerError;
|
||||
import io.kamax.mxisd.proxy.Response;
|
||||
import io.kamax.mxisd.util.OptionalUtil;
|
||||
import io.kamax.mxisd.util.RestClientUtils;
|
||||
import io.undertow.server.HttpHandler;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
@@ -55,6 +56,24 @@ public abstract class BasicHttpHandler implements HttpHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(BasicHttpHandler.class);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
protected String getAccessToken(HttpServerExchange exchange) {
|
||||
return Optional.ofNullable(exchange.getRequestHeaders().getFirst("Authorization"))
|
||||
.flatMap(v -> {
|
||||
|
@@ -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;
|
||||
|
||||
import io.kamax.mxisd.auth.AccountManager;
|
||||
import io.kamax.mxisd.config.PolicyConfig;
|
||||
import io.kamax.mxisd.exception.InvalidCredentialsException;
|
||||
import io.kamax.mxisd.storage.ormlite.dao.AccountDao;
|
||||
import io.undertow.server.HttpHandler;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CheckTermsHandler extends BasicHttpHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(CheckTermsHandler.class);
|
||||
|
||||
private final AccountManager accountManager;
|
||||
|
||||
private final HttpHandler child;
|
||||
|
||||
private final List<PolicyConfig.PolicyObject> policies;
|
||||
|
||||
public static CheckTermsHandler around(AccountManager accountManager, HttpHandler child, List<PolicyConfig.PolicyObject> policies) {
|
||||
return new CheckTermsHandler(accountManager, child, policies);
|
||||
}
|
||||
|
||||
private CheckTermsHandler(AccountManager accountManager, HttpHandler child,
|
||||
List<PolicyConfig.PolicyObject> policies) {
|
||||
this.accountManager = accountManager;
|
||||
this.child = child;
|
||||
this.policies = policies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
||||
String token = findAccessToken(exchange).orElse(null);
|
||||
if (token == null) {
|
||||
log.error("Unauthorized request from: {}", exchange.getHostAndPort());
|
||||
throw new InvalidCredentialsException();
|
||||
}
|
||||
|
||||
if (!accountManager.isTermAccepted(token, policies)) {
|
||||
log.error("Non accepting request from: {}", exchange.getHostAndPort());
|
||||
throw new InvalidCredentialsException();
|
||||
}
|
||||
log.trace("Access granted");
|
||||
child.handleRequest(exchange);
|
||||
}
|
||||
}
|
@@ -21,35 +21,11 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -87,6 +87,10 @@ public class SaneHandler extends BasicHttpHandler {
|
||||
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 (InvalidPepperException e) {
|
||||
respond(exchange, HttpStatus.SC_BAD_REQUEST, "M_INVALID_PEPPER", e.getMessage());
|
||||
} catch (InvalidParamException e) {
|
||||
respond(exchange, HttpStatus.SC_BAD_REQUEST, "M_INVALID_PARAM", e.getMessage());
|
||||
} catch (FeatureNotAvailable e) {
|
||||
if (StringUtils.isNotBlank(e.getInternalReason())) {
|
||||
log.error("Feature not available: {}", e.getInternalReason());
|
||||
|
@@ -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.matrix.json.GsonUtil;
|
||||
import io.kamax.mxisd.auth.AccountManager;
|
||||
import io.kamax.mxisd.exception.InvalidCredentialsException;
|
||||
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 LOGGER = LoggerFactory.getLogger(AccountGetUserInfoHandler.class);
|
||||
|
||||
private final AccountManager accountManager;
|
||||
|
||||
public AccountGetUserInfoHandler(AccountManager accountManager) {
|
||||
this.accountManager = accountManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequest(HttpServerExchange exchange) {
|
||||
LOGGER.info("Get User Info.");
|
||||
String token = findAccessToken(exchange).orElseThrow(InvalidCredentialsException::new);
|
||||
|
||||
String userId = accountManager.getUserId(token);
|
||||
LOGGER.info("Account found: {}", userId);
|
||||
respond(exchange, GsonUtil.makeObj("user_id", userId));
|
||||
}
|
||||
}
|
@@ -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.auth.v2;
|
||||
|
||||
import io.kamax.mxisd.auth.AccountManager;
|
||||
import io.kamax.mxisd.exception.InvalidCredentialsException;
|
||||
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/logout";
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AccountLogoutHandler.class);
|
||||
|
||||
private final AccountManager accountManager;
|
||||
|
||||
public AccountLogoutHandler(AccountManager accountManager) {
|
||||
this.accountManager = accountManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequest(HttpServerExchange exchange) {
|
||||
LOGGER.info("Logout.");
|
||||
String token = findAccessToken(exchange).orElseThrow(InvalidCredentialsException::new);
|
||||
|
||||
accountManager.logout(token);
|
||||
|
||||
respondJson(exchange, "{}");
|
||||
}
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.auth.OpenIdToken;
|
||||
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class AccountRegisterHandler extends BasicHttpHandler {
|
||||
|
||||
public static final String Path = "/_matrix/identity/v2/account/register";
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AccountRegisterHandler.class);
|
||||
|
||||
private final AccountManager accountManager;
|
||||
|
||||
public AccountRegisterHandler(AccountManager accountManager) {
|
||||
this.accountManager = accountManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequest(HttpServerExchange exchange) {
|
||||
OpenIdToken openIdToken = parseJsonTo(exchange, OpenIdToken.class);
|
||||
|
||||
if (LOGGER.isInfoEnabled()) {
|
||||
LOGGER.info("Registration from domain: {}, expired at {}", openIdToken.getMatrixServerName(),
|
||||
new Date(openIdToken.getExpiredIn()));
|
||||
}
|
||||
|
||||
String token = accountManager.register(openIdToken);
|
||||
respond(exchange, GsonUtil.makeObj("token", token));
|
||||
}
|
||||
}
|
@@ -18,18 +18,16 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.share;
|
||||
|
||||
import io.kamax.mxisd.crypto.KeyManager;
|
||||
import io.kamax.mxisd.crypto.KeyType;
|
||||
import io.kamax.mxisd.http.IsAPIv1;
|
||||
import io.kamax.mxisd.http.undertow.handler.ApiHandler;
|
||||
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";
|
||||
public class EphemeralKeyIsValidHandler extends KeyIsValidHandler implements ApiHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(EphemeralKeyIsValidHandler.class);
|
||||
|
||||
@@ -48,4 +46,8 @@ public class EphemeralKeyIsValidHandler extends KeyIsValidHandler {
|
||||
respondJson(exchange, mgr.isValid(KeyType.Ephemeral, pubKey) ? validKey : invalidKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHandlerPath() {
|
||||
return "/pubkey/ephemeral/isvalid";
|
||||
}
|
||||
}
|
@@ -18,19 +18,21 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.share;
|
||||
|
||||
import io.kamax.mxisd.http.IsAPIv1;
|
||||
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.ApiHandler;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
|
||||
public class HelloHandler extends BasicHttpHandler {
|
||||
|
||||
public static final String Path = IsAPIv1.Base;
|
||||
public class HelloHandler extends BasicHttpHandler implements ApiHandler {
|
||||
|
||||
@Override
|
||||
public void handleRequest(HttpServerExchange exchange) {
|
||||
respondJson(exchange, "{}");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHandlerPath() {
|
||||
return "";
|
||||
}
|
||||
}
|
@@ -18,23 +18,22 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.share;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import io.kamax.mxisd.crypto.GenericKeyIdentifier;
|
||||
import io.kamax.mxisd.crypto.KeyManager;
|
||||
import io.kamax.mxisd.crypto.KeyType;
|
||||
import io.kamax.mxisd.http.IsAPIv1;
|
||||
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.ApiHandler;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class KeyGetHandler extends BasicHttpHandler {
|
||||
public class KeyGetHandler extends BasicHttpHandler implements ApiHandler {
|
||||
|
||||
public static final String Key = "key";
|
||||
public static final String Path = IsAPIv1.Base + "/pubkey/{" + Key + "}";
|
||||
|
||||
private transient final Logger log = LoggerFactory.getLogger(KeyGetHandler.class);
|
||||
|
||||
@@ -61,4 +60,8 @@ public class KeyGetHandler extends BasicHttpHandler {
|
||||
respond(exchange, obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHandlerPath() {
|
||||
return "/pubkey/{" + Key + "}";
|
||||
}
|
||||
}
|
@@ -18,7 +18,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.share;
|
||||
|
||||
import io.kamax.matrix.json.GsonUtil;
|
||||
import io.kamax.mxisd.http.io.identity.KeyValidityJson;
|
@@ -18,7 +18,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.share;
|
||||
|
||||
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
|
||||
import io.kamax.mxisd.lookup.ALookupRequest;
|
@@ -18,18 +18,16 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.share;
|
||||
|
||||
import io.kamax.mxisd.crypto.KeyManager;
|
||||
import io.kamax.mxisd.crypto.KeyType;
|
||||
import io.kamax.mxisd.http.IsAPIv1;
|
||||
import io.kamax.mxisd.http.undertow.handler.ApiHandler;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class RegularKeyIsValidHandler extends KeyIsValidHandler {
|
||||
|
||||
public static final String Path = IsAPIv1.Base + "/pubkey/isvalid";
|
||||
public class RegularKeyIsValidHandler extends KeyIsValidHandler implements ApiHandler {
|
||||
|
||||
private transient final Logger log = LoggerFactory.getLogger(RegularKeyIsValidHandler.class);
|
||||
|
||||
@@ -48,4 +46,8 @@ public class RegularKeyIsValidHandler extends KeyIsValidHandler {
|
||||
respondJson(exchange, mgr.isValid(KeyType.Regular, pubKey) ? validKey : invalidKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHandlerPath() {
|
||||
return "/pubkey/isvalid";
|
||||
}
|
||||
}
|
@@ -18,26 +18,25 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.share;
|
||||
|
||||
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.http.undertow.handler.ApiHandler;
|
||||
import io.kamax.mxisd.session.SessionManager;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class SessionStartHandler extends BasicHttpHandler {
|
||||
public class SessionStartHandler extends BasicHttpHandler implements ApiHandler {
|
||||
|
||||
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);
|
||||
|
||||
@@ -84,4 +83,8 @@ public class SessionStartHandler extends BasicHttpHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHandlerPath() {
|
||||
return "/validate/{" + Medium + "}/requestToken";
|
||||
}
|
||||
}
|
@@ -18,16 +18,16 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.share;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import io.kamax.matrix.json.GsonUtil;
|
||||
import io.kamax.mxisd.crypto.SignatureManager;
|
||||
import io.kamax.mxisd.exception.BadRequestException;
|
||||
import io.kamax.mxisd.http.IsAPIv1;
|
||||
import io.kamax.mxisd.http.io.identity.BindRequest;
|
||||
import io.kamax.mxisd.http.io.identity.SingeLookupReplyJson;
|
||||
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.ApiHandler;
|
||||
import io.kamax.mxisd.invitation.InvitationManager;
|
||||
import io.kamax.mxisd.lookup.SingleLookupReply;
|
||||
import io.kamax.mxisd.session.SessionManager;
|
||||
@@ -42,9 +42,7 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.Deque;
|
||||
import java.util.Map;
|
||||
|
||||
public class SessionTpidBindHandler extends BasicHttpHandler {
|
||||
|
||||
public static final String Path = IsAPIv1.Base + "/3pid/bind";
|
||||
public class SessionTpidBindHandler extends BasicHttpHandler implements ApiHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SessionTpidBindHandler.class);
|
||||
|
||||
@@ -97,4 +95,8 @@ public class SessionTpidBindHandler extends BasicHttpHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHandlerPath() {
|
||||
return "/3pid/bind";
|
||||
}
|
||||
}
|
@@ -18,21 +18,19 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.share;
|
||||
|
||||
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.http.undertow.handler.ApiHandler;
|
||||
import io.kamax.mxisd.lookup.ThreePidValidation;
|
||||
import io.kamax.mxisd.session.SessionManager;
|
||||
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";
|
||||
public class SessionTpidGetValidatedHandler extends BasicHttpHandler implements ApiHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SessionTpidGetValidatedHandler.class);
|
||||
|
||||
@@ -62,4 +60,8 @@ public class SessionTpidGetValidatedHandler extends BasicHttpHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHandlerPath() {
|
||||
return "/3pid/getValidated3pid";
|
||||
}
|
||||
}
|
@@ -18,19 +18,17 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.share;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import io.kamax.mxisd.http.IsAPIv1;
|
||||
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.ApiHandler;
|
||||
import io.kamax.mxisd.session.SessionManager;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class SessionTpidUnbindHandler extends BasicHttpHandler {
|
||||
|
||||
public static final String Path = IsAPIv1.Base + "/3pid/unbind";
|
||||
public class SessionTpidUnbindHandler extends BasicHttpHandler implements ApiHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SessionTpidUnbindHandler.class);
|
||||
|
||||
@@ -48,4 +46,9 @@ public class SessionTpidUnbindHandler extends BasicHttpHandler {
|
||||
sessionMgr.unbind(auth, body);
|
||||
writeBodyAsUtf8(exchange, "{}");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHandlerPath() {
|
||||
return "/3pid/unbind";
|
||||
}
|
||||
}
|
@@ -18,19 +18,17 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.share;
|
||||
|
||||
import io.kamax.mxisd.http.IsAPIv1;
|
||||
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.ApiHandler;
|
||||
import io.kamax.mxisd.session.SessionManager;
|
||||
import io.kamax.mxisd.session.ValidationResult;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public abstract class SessionValidateHandler extends BasicHttpHandler {
|
||||
|
||||
public static final String Path = IsAPIv1.Base + "/validate/{medium}/submitToken";
|
||||
public abstract class SessionValidateHandler extends BasicHttpHandler implements ApiHandler {
|
||||
|
||||
private transient final Logger log = LoggerFactory.getLogger(SessionValidateHandler.class);
|
||||
|
||||
@@ -52,4 +50,8 @@ public abstract class SessionValidateHandler extends BasicHttpHandler {
|
||||
return mgr.validate(sid, secret, token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHandlerPath() {
|
||||
return "/validate/{medium}/submitToken";
|
||||
}
|
||||
}
|
@@ -18,7 +18,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.share;
|
||||
|
||||
import io.kamax.mxisd.config.MxisdConfig;
|
||||
import io.kamax.mxisd.config.ServerConfig;
|
@@ -18,7 +18,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.share;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import io.kamax.matrix.json.GsonUtil;
|
@@ -18,7 +18,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.share;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import io.kamax.matrix.MatrixID;
|
||||
@@ -27,17 +27,15 @@ import io.kamax.matrix.json.GsonUtil;
|
||||
import io.kamax.matrix.json.MatrixJson;
|
||||
import io.kamax.mxisd.config.MxisdConfig;
|
||||
import io.kamax.mxisd.crypto.SignatureManager;
|
||||
import io.kamax.mxisd.http.IsAPIv1;
|
||||
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.ApiHandler;
|
||||
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
||||
import io.kamax.mxisd.invitation.InvitationManager;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class SignEd25519Handler extends BasicHttpHandler {
|
||||
|
||||
public static final String Path = IsAPIv1.Base + "/sign-ed25519";
|
||||
public class SignEd25519Handler extends BasicHttpHandler implements ApiHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SignEd25519Handler.class);
|
||||
|
||||
@@ -72,4 +70,8 @@ public class SignEd25519Handler extends BasicHttpHandler {
|
||||
respondJson(exchange, res);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHandlerPath() {
|
||||
return "/sign-ed25519";
|
||||
}
|
||||
}
|
@@ -18,7 +18,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.share;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
@@ -28,10 +28,10 @@ import io.kamax.matrix.json.GsonUtil;
|
||||
import io.kamax.mxisd.config.ServerConfig;
|
||||
import io.kamax.mxisd.crypto.KeyManager;
|
||||
import io.kamax.mxisd.exception.BadRequestException;
|
||||
import io.kamax.mxisd.http.IsAPIv1;
|
||||
import io.kamax.mxisd.http.io.identity.StoreInviteRequest;
|
||||
import io.kamax.mxisd.http.io.identity.ThreePidInviteReplyIO;
|
||||
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.ApiHandler;
|
||||
import io.kamax.mxisd.invitation.IThreePidInvite;
|
||||
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
||||
import io.kamax.mxisd.invitation.InvitationManager;
|
||||
@@ -45,9 +45,7 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.Deque;
|
||||
import java.util.Map;
|
||||
|
||||
public class StoreInviteHandler extends BasicHttpHandler {
|
||||
|
||||
public static final String Path = IsAPIv1.Base + "/store-invite";
|
||||
public class StoreInviteHandler extends BasicHttpHandler implements ApiHandler {
|
||||
|
||||
private ServerConfig cfg;
|
||||
private InvitationManager invMgr;
|
||||
@@ -100,4 +98,8 @@ public class StoreInviteHandler extends BasicHttpHandler {
|
||||
respondJson(exchange, new ThreePidInviteReplyIO(reply, keyMgr.getPublicKeyBase64(keyMgr.getServerSigningKey().getId()), cfg.getPublicUrl()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHandlerPath() {
|
||||
return "/store-invite";
|
||||
}
|
||||
}
|
@@ -23,6 +23,8 @@ 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.http.undertow.handler.ApiHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.identity.share.LookupHandler;
|
||||
import io.kamax.mxisd.lookup.BulkLookupRequest;
|
||||
import io.kamax.mxisd.lookup.ThreePidMapping;
|
||||
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
|
||||
@@ -33,7 +35,7 @@ import org.slf4j.LoggerFactory;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class BulkLookupHandler extends LookupHandler {
|
||||
public class BulkLookupHandler extends LookupHandler implements ApiHandler {
|
||||
|
||||
public static final String Path = IsAPIv1.Base + "/bulk_lookup";
|
||||
|
||||
@@ -69,4 +71,8 @@ public class BulkLookupHandler extends LookupHandler {
|
||||
respondJson(exchange, answer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHandlerPath() {
|
||||
return "/bulk_lookup";
|
||||
}
|
||||
}
|
||||
|
@@ -27,6 +27,8 @@ import io.kamax.mxisd.config.ServerConfig;
|
||||
import io.kamax.mxisd.crypto.SignatureManager;
|
||||
import io.kamax.mxisd.http.IsAPIv1;
|
||||
import io.kamax.mxisd.http.io.identity.SingeLookupReplyJson;
|
||||
import io.kamax.mxisd.http.undertow.handler.ApiHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.identity.share.LookupHandler;
|
||||
import io.kamax.mxisd.lookup.SingleLookupReply;
|
||||
import io.kamax.mxisd.lookup.SingleLookupRequest;
|
||||
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
|
||||
@@ -36,7 +38,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class SingleLookupHandler extends LookupHandler {
|
||||
public class SingleLookupHandler extends LookupHandler implements ApiHandler {
|
||||
|
||||
public static final String Path = IsAPIv1.Base + "/lookup";
|
||||
|
||||
@@ -77,4 +79,8 @@ public class SingleLookupHandler extends LookupHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHandlerPath() {
|
||||
return "/lookup";
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,38 @@
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.v2;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import io.kamax.mxisd.config.HashingConfig;
|
||||
import io.kamax.mxisd.hash.HashManager;
|
||||
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
|
||||
public class HashDetailsHandler extends BasicHttpHandler {
|
||||
|
||||
public static final String PATH = "/_matrix/identity/v2/hash_details";
|
||||
|
||||
private final HashManager hashManager;
|
||||
|
||||
public HashDetailsHandler(HashManager hashManager) {
|
||||
this.hashManager = hashManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
||||
respond(exchange, getResponse());
|
||||
}
|
||||
|
||||
private JsonObject getResponse() {
|
||||
JsonObject response = new JsonObject();
|
||||
response.addProperty("lookup_pepper", hashManager.getHashEngine().getPepper());
|
||||
JsonArray algorithms = new JsonArray();
|
||||
HashingConfig config = hashManager.getConfig();
|
||||
if (config.isEnabled()) {
|
||||
for (HashingConfig.Algorithm algorithm : config.getAlgorithms()) {
|
||||
algorithms.add(algorithm.name().toLowerCase());
|
||||
}
|
||||
}
|
||||
response.add("algorithms", algorithms);
|
||||
return response;
|
||||
}
|
||||
}
|
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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.v2;
|
||||
|
||||
import io.kamax.mxisd.config.HashingConfig;
|
||||
import io.kamax.mxisd.exception.InvalidParamException;
|
||||
import io.kamax.mxisd.exception.InvalidPepperException;
|
||||
import io.kamax.mxisd.hash.HashManager;
|
||||
import io.kamax.mxisd.http.IsAPIv2;
|
||||
import io.kamax.mxisd.http.io.identity.ClientHashLookupAnswer;
|
||||
import io.kamax.mxisd.http.io.identity.ClientHashLookupRequest;
|
||||
import io.kamax.mxisd.http.undertow.handler.ApiHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.identity.share.LookupHandler;
|
||||
import io.kamax.mxisd.lookup.BulkLookupRequest;
|
||||
import io.kamax.mxisd.lookup.HashLookupRequest;
|
||||
import io.kamax.mxisd.lookup.ThreePidMapping;
|
||||
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class HashLookupHandler extends LookupHandler implements ApiHandler {
|
||||
|
||||
public static final String Path = IsAPIv2.Base + "/lookup";
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(HashLookupHandler.class);
|
||||
|
||||
private LookupStrategy strategy;
|
||||
private HashManager hashManager;
|
||||
|
||||
public HashLookupHandler(LookupStrategy strategy, HashManager hashManager) {
|
||||
this.strategy = strategy;
|
||||
this.hashManager = hashManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
||||
ClientHashLookupRequest input = parseJsonTo(exchange, ClientHashLookupRequest.class);
|
||||
HashLookupRequest lookupRequest = new HashLookupRequest();
|
||||
setRequesterInfo(lookupRequest, exchange);
|
||||
log.info("Got bulk lookup request from {} with client {} - Is recursive? {}",
|
||||
lookupRequest.getRequester(), lookupRequest.getUserAgent(), lookupRequest.isRecursive());
|
||||
|
||||
if (!hashManager.getConfig().isEnabled()) {
|
||||
throw new InvalidParamException();
|
||||
}
|
||||
|
||||
if (!hashManager.getHashEngine().getPepper().equals(input.getPepper())) {
|
||||
throw new InvalidPepperException();
|
||||
}
|
||||
|
||||
switch (input.getAlgorithm()) {
|
||||
case "none":
|
||||
noneAlgorithm(exchange, lookupRequest, input);
|
||||
break;
|
||||
case "sha256":
|
||||
sha256Algorithm(exchange, lookupRequest, input);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidParamException();
|
||||
}
|
||||
}
|
||||
|
||||
private void noneAlgorithm(HttpServerExchange exchange, HashLookupRequest request, ClientHashLookupRequest input) throws Exception {
|
||||
if (!hashManager.getConfig().getAlgorithms().contains(HashingConfig.Algorithm.NONE)) {
|
||||
throw new InvalidParamException();
|
||||
}
|
||||
|
||||
BulkLookupRequest bulkLookupRequest = new BulkLookupRequest();
|
||||
List<ThreePidMapping> mappings = new ArrayList<>();
|
||||
for (String address : input.getAddresses()) {
|
||||
String[] parts = address.split(" ");
|
||||
ThreePidMapping mapping = new ThreePidMapping();
|
||||
mapping.setMedium(parts[0]);
|
||||
mapping.setValue(parts[1]);
|
||||
mappings.add(mapping);
|
||||
}
|
||||
bulkLookupRequest.setMappings(mappings);
|
||||
|
||||
ClientHashLookupAnswer answer = new ClientHashLookupAnswer();
|
||||
|
||||
for (ThreePidMapping mapping : strategy.find(bulkLookupRequest).get()) {
|
||||
answer.getMappings().put(mapping.getMedium() + " " + mapping.getValue(), mapping.getMxid());
|
||||
}
|
||||
log.info("Finished bulk lookup request from {}", request.getRequester());
|
||||
|
||||
respondJson(exchange, answer);
|
||||
}
|
||||
|
||||
private void sha256Algorithm(HttpServerExchange exchange, HashLookupRequest request, ClientHashLookupRequest input) {
|
||||
if (!hashManager.getConfig().getAlgorithms().contains(HashingConfig.Algorithm.SHA256)) {
|
||||
throw new InvalidParamException();
|
||||
}
|
||||
|
||||
ClientHashLookupAnswer answer = new ClientHashLookupAnswer();
|
||||
for (Pair<String, ThreePidMapping> pair : hashManager.getHashStorage().find(request.getHashes())) {
|
||||
answer.getMappings().put(pair.getKey(), pair.getValue().getMxid());
|
||||
}
|
||||
log.info("Finished bulk lookup request from {}", request.getRequester());
|
||||
|
||||
respondJson(exchange, answer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHandlerPath() {
|
||||
return "/bulk_lookup";
|
||||
}
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
package io.kamax.mxisd.http.undertow.handler.term.v2;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import io.kamax.matrix.json.GsonUtil;
|
||||
import io.kamax.mxisd.auth.AccountManager;
|
||||
import io.kamax.mxisd.exception.InvalidCredentialsException;
|
||||
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
|
||||
import io.kamax.mxisd.storage.ormlite.dao.AccountDao;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class AcceptTermsHandler extends BasicHttpHandler {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AcceptTermsHandler.class);
|
||||
|
||||
public static final String PATH = "/_matrix/identity/v2/terms";
|
||||
|
||||
private final AccountManager accountManager;
|
||||
|
||||
public AcceptTermsHandler(AccountManager accountManager) {
|
||||
this.accountManager = accountManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
||||
String token = getAccessToken(exchange);
|
||||
|
||||
JsonObject request = parseJsonObject(exchange);
|
||||
JsonObject accepts = GsonUtil.getObj(request, "user_accepts");
|
||||
AccountDao account = accountManager.findAccount(token);
|
||||
|
||||
if (account == null) {
|
||||
throw new InvalidCredentialsException();
|
||||
}
|
||||
|
||||
if (accepts == null || accepts.isJsonNull()) {
|
||||
respondJson(exchange, "{}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (accepts.isJsonArray()) {
|
||||
for (JsonElement acceptItem : accepts.getAsJsonArray()) {
|
||||
String termUrl = acceptItem.getAsString();
|
||||
LOGGER.info("User {} accepts the term: {}", account.getUserId(), termUrl);
|
||||
accountManager.acceptTerm(token, termUrl);
|
||||
}
|
||||
} else {
|
||||
String termUrl = accepts.getAsString();
|
||||
LOGGER.info("User {} accepts the term: {}", account.getUserId(), termUrl);
|
||||
accountManager.acceptTerm(token, termUrl);
|
||||
}
|
||||
|
||||
respondJson(exchange, "{}");
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
package io.kamax.mxisd.http.undertow.handler.term.v2;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import io.kamax.mxisd.config.PolicyConfig;
|
||||
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class GetTermsHandler extends BasicHttpHandler {
|
||||
|
||||
public static final String PATH = "/_matrix/identity/v2/terms";
|
||||
|
||||
private final JsonObject policyResponse;
|
||||
|
||||
public GetTermsHandler(PolicyConfig config) {
|
||||
policyResponse = new JsonObject();
|
||||
JsonObject policies = new JsonObject();
|
||||
for (Map.Entry<String, PolicyConfig.PolicyObject> policyItem : config.getPolicies().entrySet()) {
|
||||
JsonObject policy = new JsonObject();
|
||||
policy.addProperty("version", policyItem.getValue().getVersion());
|
||||
for (Map.Entry<String, PolicyConfig.TermObject> termEntry : policyItem.getValue().getTerms().entrySet()) {
|
||||
JsonObject term = new JsonObject();
|
||||
term.addProperty("name", termEntry.getValue().getName());
|
||||
term.addProperty("url", termEntry.getValue().getUrl());
|
||||
policy.add(termEntry.getKey(), term);
|
||||
}
|
||||
policies.add(policyItem.getKey(), policy);
|
||||
}
|
||||
policyResponse.add("policies", policies);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
||||
respond(exchange, policyResponse);
|
||||
}
|
||||
}
|
16
src/main/java/io/kamax/mxisd/lookup/HashLookupRequest.java
Normal file
16
src/main/java/io/kamax/mxisd/lookup/HashLookupRequest.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package io.kamax.mxisd.lookup;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class HashLookupRequest extends ALookupRequest {
|
||||
|
||||
private List<String> hashes;
|
||||
|
||||
public List<String> getHashes() {
|
||||
return hashes;
|
||||
}
|
||||
|
||||
public void setHashes(List<String> hashes) {
|
||||
this.hashes = hashes;
|
||||
}
|
||||
}
|
@@ -24,6 +24,7 @@ import io.kamax.mxisd.lookup.SingleLookupReply;
|
||||
import io.kamax.mxisd.lookup.SingleLookupRequest;
|
||||
import io.kamax.mxisd.lookup.ThreePidMapping;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -40,4 +41,7 @@ public interface IThreePidProvider {
|
||||
|
||||
List<ThreePidMapping> populate(List<ThreePidMapping> mappings);
|
||||
|
||||
default Iterable<ThreePidMapping> populateHashes() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@
|
||||
package io.kamax.mxisd.lookup.strategy;
|
||||
|
||||
import io.kamax.mxisd.lookup.BulkLookupRequest;
|
||||
import io.kamax.mxisd.lookup.HashLookupRequest;
|
||||
import io.kamax.mxisd.lookup.SingleLookupReply;
|
||||
import io.kamax.mxisd.lookup.SingleLookupRequest;
|
||||
import io.kamax.mxisd.lookup.ThreePidMapping;
|
||||
@@ -46,4 +47,5 @@ public interface LookupStrategy {
|
||||
|
||||
CompletableFuture<List<ThreePidMapping>> find(BulkLookupRequest requests);
|
||||
|
||||
CompletableFuture<List<ThreePidMapping>> find(HashLookupRequest request);
|
||||
}
|
||||
|
@@ -25,10 +25,13 @@ import io.kamax.matrix.json.GsonUtil;
|
||||
import io.kamax.matrix.json.MatrixJson;
|
||||
import io.kamax.mxisd.config.MxisdConfig;
|
||||
import io.kamax.mxisd.exception.ConfigurationException;
|
||||
import io.kamax.mxisd.hash.HashManager;
|
||||
import io.kamax.mxisd.hash.storage.HashStorage;
|
||||
import io.kamax.mxisd.lookup.*;
|
||||
import io.kamax.mxisd.lookup.fetcher.IBridgeFetcher;
|
||||
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -50,10 +53,14 @@ public class RecursivePriorityLookupStrategy implements LookupStrategy {
|
||||
|
||||
private List<CIDRUtils> allowedCidr = new ArrayList<>();
|
||||
|
||||
public RecursivePriorityLookupStrategy(MxisdConfig.Lookup cfg, List<? extends IThreePidProvider> providers, IBridgeFetcher bridge) {
|
||||
private HashManager hashManager;
|
||||
|
||||
public RecursivePriorityLookupStrategy(MxisdConfig.Lookup cfg, List<? extends IThreePidProvider> providers, IBridgeFetcher bridge,
|
||||
HashManager hashManager) {
|
||||
this.cfg = cfg;
|
||||
this.bridge = bridge;
|
||||
this.providers = new ArrayList<>(providers);
|
||||
this.hashManager = hashManager;
|
||||
|
||||
try {
|
||||
log.info("Found {} providers", providers.size());
|
||||
@@ -65,6 +72,8 @@ public class RecursivePriorityLookupStrategy implements LookupStrategy {
|
||||
log.info("{} is allowed for recursion", cidr);
|
||||
allowedCidr.add(new CIDRUtils(cidr));
|
||||
}
|
||||
|
||||
log.info("Hash lookups enabled: {}", hashManager.getConfig().isEnabled());
|
||||
} catch (UnknownHostException e) {
|
||||
throw new ConfigurationException("lookup.recursive.allowedCidrs", "Allowed CIDRs");
|
||||
}
|
||||
@@ -230,4 +239,12 @@ public class RecursivePriorityLookupStrategy implements LookupStrategy {
|
||||
return bulkLookupInProgress.remove(payloadId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<ThreePidMapping>> find(HashLookupRequest request) {
|
||||
HashStorage hashStorage = hashManager.getHashStorage();
|
||||
CompletableFuture<List<ThreePidMapping>> result = new CompletableFuture<>();
|
||||
result.complete(hashStorage.find(request.getHashes()).stream().map(Pair::getValue).collect(Collectors.toList()));
|
||||
hashManager.getRotationStrategy().newRequest();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,9 @@
|
||||
package io.kamax.mxisd.matrix;
|
||||
|
||||
public enum IdentityServiceAPI {
|
||||
|
||||
@Deprecated
|
||||
V1,
|
||||
|
||||
V2
|
||||
}
|
@@ -57,6 +57,8 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
@@ -240,12 +242,26 @@ public class SessionManager {
|
||||
}
|
||||
}
|
||||
|
||||
private String getDomain(String publicUrl) {
|
||||
URL url;
|
||||
try {
|
||||
url = new URL(publicUrl);
|
||||
} catch (MalformedURLException e) {
|
||||
log.error("Malformed public url, use as is");
|
||||
return publicUrl;
|
||||
}
|
||||
int port = url.getPort();
|
||||
return url.getHost() + (port != -1 ? ":" + url.getPort() : "");
|
||||
}
|
||||
|
||||
private void checkAuthorization(String auth, JsonObject reqData) {
|
||||
if (!auth.startsWith("X-Matrix ")) {
|
||||
throw new NotAllowedException("Wrong authorization header");
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(cfg.getServer().getPublicUrl())) {
|
||||
String domain = getDomain(cfg.getServer().getPublicUrl());
|
||||
|
||||
if (StringUtils.isBlank(domain)) {
|
||||
throw new NotAllowedException("Unable to verify request, missing `server.publicUrl` property");
|
||||
}
|
||||
|
||||
@@ -287,7 +303,7 @@ public class SessionManager {
|
||||
jsonObject.addProperty("method", "POST");
|
||||
jsonObject.addProperty("uri", "/_matrix/identity/api/v1/3pid/unbind");
|
||||
jsonObject.addProperty("origin", origin);
|
||||
jsonObject.addProperty("destination_is", URI.create(cfg.getServer().getPublicUrl()).getHost());
|
||||
jsonObject.addProperty("destination_is", domain);
|
||||
jsonObject.add("content", reqData);
|
||||
|
||||
String canonical = MatrixJson.encodeCanonical(jsonObject);
|
||||
|
@@ -21,13 +21,18 @@
|
||||
package io.kamax.mxisd.storage;
|
||||
|
||||
import io.kamax.matrix.ThreePid;
|
||||
import io.kamax.mxisd.config.PolicyConfig;
|
||||
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
||||
import io.kamax.mxisd.lookup.ThreePidMapping;
|
||||
import io.kamax.mxisd.storage.dao.IThreePidSessionDao;
|
||||
import io.kamax.mxisd.storage.ormlite.dao.ASTransactionDao;
|
||||
import io.kamax.mxisd.storage.ormlite.dao.AccountDao;
|
||||
import io.kamax.mxisd.storage.ormlite.dao.ThreePidInviteIO;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface IStorage {
|
||||
@@ -52,4 +57,21 @@ public interface IStorage {
|
||||
|
||||
Optional<ASTransactionDao> getTransactionResult(String localpart, String txnId);
|
||||
|
||||
void insertToken(AccountDao accountDao);
|
||||
|
||||
Optional<AccountDao> findAccount(String token);
|
||||
|
||||
void deleteToken(String token);
|
||||
|
||||
void acceptTerm(String token, String url);
|
||||
|
||||
void deleteAccepts(String token);
|
||||
|
||||
boolean isTermAccepted(String token, List<PolicyConfig.PolicyObject> policies);
|
||||
|
||||
void clearHashes();
|
||||
|
||||
void addHash(String mxid, String medium, String address, String hash);
|
||||
|
||||
Collection<Pair<String, ThreePidMapping>> findHashes(Iterable<String> hashes);
|
||||
}
|
||||
|
@@ -24,25 +24,38 @@ import com.j256.ormlite.dao.CloseableWrappedIterable;
|
||||
import com.j256.ormlite.dao.Dao;
|
||||
import com.j256.ormlite.dao.DaoManager;
|
||||
import com.j256.ormlite.jdbc.JdbcConnectionSource;
|
||||
import com.j256.ormlite.stmt.QueryBuilder;
|
||||
import com.j256.ormlite.support.ConnectionSource;
|
||||
import com.j256.ormlite.table.TableUtils;
|
||||
import io.kamax.matrix.ThreePid;
|
||||
import io.kamax.mxisd.config.MxisdConfig;
|
||||
import io.kamax.mxisd.config.PolicyConfig;
|
||||
import io.kamax.mxisd.exception.ConfigurationException;
|
||||
import io.kamax.mxisd.exception.InternalServerError;
|
||||
import io.kamax.mxisd.exception.InvalidCredentialsException;
|
||||
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
||||
import io.kamax.mxisd.lookup.ThreePidMapping;
|
||||
import io.kamax.mxisd.storage.IStorage;
|
||||
import io.kamax.mxisd.storage.dao.IThreePidSessionDao;
|
||||
import io.kamax.mxisd.storage.ormlite.dao.ASTransactionDao;
|
||||
import io.kamax.mxisd.storage.ormlite.dao.AccountDao;
|
||||
import io.kamax.mxisd.storage.ormlite.dao.HashDao;
|
||||
import io.kamax.mxisd.storage.ormlite.dao.HistoricalThreePidInviteIO;
|
||||
import io.kamax.mxisd.storage.ormlite.dao.AcceptedDao;
|
||||
import io.kamax.mxisd.storage.ormlite.dao.ThreePidInviteIO;
|
||||
import io.kamax.mxisd.storage.ormlite.dao.ThreePidSessionDao;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class OrmLiteSqlStorage implements IStorage {
|
||||
|
||||
@@ -64,6 +77,9 @@ public class OrmLiteSqlStorage implements IStorage {
|
||||
private Dao<HistoricalThreePidInviteIO, String> expInvDao;
|
||||
private Dao<ThreePidSessionDao, String> sessionDao;
|
||||
private Dao<ASTransactionDao, String> asTxnDao;
|
||||
private Dao<AccountDao, String> accountDao;
|
||||
private Dao<AcceptedDao, String> acceptedDao;
|
||||
private Dao<HashDao, String> hashDao;
|
||||
|
||||
public OrmLiteSqlStorage(MxisdConfig cfg) {
|
||||
this(cfg.getStorage().getBackend(), cfg.getStorage().getProvider().getSqlite().getDatabase());
|
||||
@@ -84,6 +100,9 @@ public class OrmLiteSqlStorage implements IStorage {
|
||||
expInvDao = createDaoAndTable(connPool, HistoricalThreePidInviteIO.class);
|
||||
sessionDao = createDaoAndTable(connPool, ThreePidSessionDao.class);
|
||||
asTxnDao = createDaoAndTable(connPool, ASTransactionDao.class);
|
||||
accountDao = createDaoAndTable(connPool, AccountDao.class);
|
||||
acceptedDao = createDaoAndTable(connPool, AcceptedDao.class);
|
||||
hashDao = createDaoAndTable(connPool, HashDao.class);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -237,4 +256,103 @@ public class OrmLiteSqlStorage implements IStorage {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertToken(AccountDao account) {
|
||||
withCatcher(() -> {
|
||||
int created = accountDao.create(account);
|
||||
if (created != 1) {
|
||||
throw new RuntimeException("Unexpected row count after DB action: " + created);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<AccountDao> findAccount(String token) {
|
||||
return withCatcher(() -> {
|
||||
List<AccountDao> accounts = accountDao.queryForEq("token", token);
|
||||
if (accounts.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
if (accounts.size() != 1) {
|
||||
throw new RuntimeException("Unexpected rows for access token: " + accounts.size());
|
||||
}
|
||||
return Optional.of(accounts.get(0));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteToken(String token) {
|
||||
withCatcher(() -> {
|
||||
int updated = accountDao.deleteById(token);
|
||||
if (updated != 1) {
|
||||
throw new RuntimeException("Unexpected row count after DB action: " + updated);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptTerm(String token, String url) {
|
||||
withCatcher(() -> {
|
||||
AccountDao account = findAccount(token).orElseThrow(InvalidCredentialsException::new);
|
||||
int created = acceptedDao.create(new AcceptedDao(url, account.getUserId(), System.currentTimeMillis()));
|
||||
if (created != 1) {
|
||||
throw new RuntimeException("Unexpected row count after DB action: " + created);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAccepts(String token) {
|
||||
withCatcher(() -> {
|
||||
AccountDao account = findAccount(token).orElseThrow(InvalidCredentialsException::new);
|
||||
acceptedDao.delete(acceptedDao.queryForEq("userId", account.getUserId()));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTermAccepted(String token, List<PolicyConfig.PolicyObject> policies) {
|
||||
return withCatcher(() -> {
|
||||
AccountDao account = findAccount(token).orElseThrow(InvalidCredentialsException::new);
|
||||
List<AcceptedDao> acceptedTerms = acceptedDao.queryForEq("userId", account.getUserId());
|
||||
for (AcceptedDao acceptedTerm : acceptedTerms) {
|
||||
for (PolicyConfig.PolicyObject policy : policies) {
|
||||
for (PolicyConfig.TermObject termObject : policy.getTerms().values()) {
|
||||
if (termObject.getUrl().equalsIgnoreCase(acceptedTerm.getUrl())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearHashes() {
|
||||
withCatcher(() -> {
|
||||
List<HashDao> allHashes = hashDao.queryForAll();
|
||||
int deleted = hashDao.delete(allHashes);
|
||||
if (deleted != allHashes.size()) {
|
||||
throw new RuntimeException("Not all hashes deleted: " + deleted);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addHash(String mxid, String medium, String address, String hash) {
|
||||
withCatcher(() -> {
|
||||
hashDao.create(new HashDao(mxid, medium, address, hash));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Pair<String, ThreePidMapping>> findHashes(Iterable<String> hashes) {
|
||||
return withCatcher(() -> {
|
||||
QueryBuilder<HashDao, String> builder = hashDao.queryBuilder();
|
||||
builder.where().in("hash", hashes);
|
||||
return hashDao.query(builder.prepare()).stream()
|
||||
.map(dao -> Pair.of(dao.getHash(), new ThreePidMapping(dao.getMedium(), dao.getAddress(), dao.getMxid()))).collect(
|
||||
Collectors.toList());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -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.storage.ormlite.dao;
|
||||
|
||||
import com.j256.ormlite.field.DatabaseField;
|
||||
import com.j256.ormlite.table.DatabaseTable;
|
||||
|
||||
@DatabaseTable(tableName = "accepted")
|
||||
public class AcceptedDao {
|
||||
|
||||
@DatabaseField(canBeNull = false, id = true)
|
||||
private String url;
|
||||
|
||||
@DatabaseField(canBeNull = false)
|
||||
private String userId;
|
||||
|
||||
@DatabaseField(canBeNull = false)
|
||||
private long acceptedAt;
|
||||
|
||||
public AcceptedDao() {
|
||||
// Needed for ORMLite
|
||||
}
|
||||
|
||||
public AcceptedDao(String url, String userId, long acceptedAt) {
|
||||
this.url = url;
|
||||
this.userId = userId;
|
||||
this.acceptedAt = acceptedAt;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public long getAcceptedAt() {
|
||||
return acceptedAt;
|
||||
}
|
||||
|
||||
public void setAcceptedAt(long acceptedAt) {
|
||||
this.acceptedAt = acceptedAt;
|
||||
}
|
||||
}
|
119
src/main/java/io/kamax/mxisd/storage/ormlite/dao/AccountDao.java
Normal file
119
src/main/java/io/kamax/mxisd/storage/ormlite/dao/AccountDao.java
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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.storage.ormlite.dao;
|
||||
|
||||
import com.j256.ormlite.field.DatabaseField;
|
||||
import com.j256.ormlite.table.DatabaseTable;
|
||||
|
||||
@DatabaseTable(tableName = "account")
|
||||
public class AccountDao {
|
||||
|
||||
@DatabaseField(canBeNull = false, id = true)
|
||||
private String token;
|
||||
|
||||
@DatabaseField(canBeNull = false)
|
||||
private String accessToken;
|
||||
|
||||
@DatabaseField(canBeNull = false)
|
||||
private String tokenType;
|
||||
|
||||
@DatabaseField(canBeNull = false)
|
||||
private String matrixServerName;
|
||||
|
||||
@DatabaseField(canBeNull = false)
|
||||
private long expiresIn;
|
||||
|
||||
@DatabaseField(canBeNull = false)
|
||||
private long createdAt;
|
||||
|
||||
@DatabaseField(canBeNull = false)
|
||||
private String userId;
|
||||
|
||||
public AccountDao() {
|
||||
// Needed for ORMLite
|
||||
}
|
||||
|
||||
public AccountDao(String accessToken, String tokenType, String matrixServerName, long expiresIn, long createdAt, String userId, String token) {
|
||||
this.accessToken = accessToken;
|
||||
this.tokenType = tokenType;
|
||||
this.matrixServerName = matrixServerName;
|
||||
this.expiresIn = expiresIn;
|
||||
this.createdAt = createdAt;
|
||||
this.userId = userId;
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public String getAccessToken() {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
public void setAccessToken(String accessToken) {
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
|
||||
public String getTokenType() {
|
||||
return tokenType;
|
||||
}
|
||||
|
||||
public void setTokenType(String tokenType) {
|
||||
this.tokenType = tokenType;
|
||||
}
|
||||
|
||||
public String getMatrixServerName() {
|
||||
return matrixServerName;
|
||||
}
|
||||
|
||||
public void setMatrixServerName(String matrixServerName) {
|
||||
this.matrixServerName = matrixServerName;
|
||||
}
|
||||
|
||||
public long getExpiresIn() {
|
||||
return expiresIn;
|
||||
}
|
||||
|
||||
public void setExpiresIn(long expiresIn) {
|
||||
this.expiresIn = expiresIn;
|
||||
}
|
||||
|
||||
public long getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(long createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
}
|
@@ -0,0 +1,62 @@
|
||||
package io.kamax.mxisd.storage.ormlite.dao;
|
||||
|
||||
import com.j256.ormlite.field.DatabaseField;
|
||||
import com.j256.ormlite.table.DatabaseTable;
|
||||
|
||||
@DatabaseTable(tableName = "hashes")
|
||||
public class HashDao {
|
||||
|
||||
@DatabaseField(canBeNull = false, id = true)
|
||||
private String mxid;
|
||||
|
||||
@DatabaseField(canBeNull = false)
|
||||
private String medium;
|
||||
|
||||
@DatabaseField(canBeNull = false)
|
||||
private String address;
|
||||
|
||||
@DatabaseField(canBeNull = false)
|
||||
private String hash;
|
||||
|
||||
public HashDao() {
|
||||
}
|
||||
|
||||
public HashDao(String mxid, String medium, String address, String hash) {
|
||||
this.mxid = mxid;
|
||||
this.medium = medium;
|
||||
this.address = address;
|
||||
this.hash = hash;
|
||||
}
|
||||
|
||||
public String getMxid() {
|
||||
return mxid;
|
||||
}
|
||||
|
||||
public void setMxid(String mxid) {
|
||||
this.mxid = mxid;
|
||||
}
|
||||
|
||||
public String getMedium() {
|
||||
return medium;
|
||||
}
|
||||
|
||||
public void setMedium(String medium) {
|
||||
this.medium = medium;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public String getHash() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
public void setHash(String hash) {
|
||||
this.hash = hash;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user