Directory integration prototype using Google Firebase auth + Synapse SQL
This commit is contained in:
@@ -110,6 +110,9 @@ dependencies {
|
|||||||
// ORMLite
|
// ORMLite
|
||||||
compile 'com.j256.ormlite:ormlite-jdbc:5.0'
|
compile 'com.j256.ormlite:ormlite-jdbc:5.0'
|
||||||
|
|
||||||
|
// Connection Pool
|
||||||
|
compile 'com.mchange:c3p0:0.9.5.2'
|
||||||
|
|
||||||
// SQLite
|
// SQLite
|
||||||
compile 'org.xerial:sqlite-jdbc:3.20.0'
|
compile 'org.xerial:sqlite-jdbc:3.20.0'
|
||||||
|
|
||||||
|
@@ -20,11 +20,6 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.backend.firebase;
|
package io.kamax.mxisd.backend.firebase;
|
||||||
|
|
||||||
import com.google.firebase.FirebaseApp;
|
|
||||||
import com.google.firebase.FirebaseOptions;
|
|
||||||
import com.google.firebase.auth.FirebaseAuth;
|
|
||||||
import com.google.firebase.auth.FirebaseCredential;
|
|
||||||
import com.google.firebase.auth.FirebaseCredentials;
|
|
||||||
import com.google.firebase.auth.UserInfo;
|
import com.google.firebase.auth.UserInfo;
|
||||||
import com.google.i18n.phonenumbers.NumberParseException;
|
import com.google.i18n.phonenumbers.NumberParseException;
|
||||||
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
||||||
@@ -38,35 +33,17 @@ import org.apache.commons.lang.StringUtils;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class GoogleFirebaseAuthenticator implements AuthenticatorProvider {
|
public class GoogleFirebaseAuthenticator extends GoogleFirebaseBackend implements AuthenticatorProvider {
|
||||||
|
|
||||||
private Logger log = LoggerFactory.getLogger(GoogleFirebaseAuthenticator.class);
|
private Logger log = LoggerFactory.getLogger(GoogleFirebaseAuthenticator.class);
|
||||||
|
|
||||||
private boolean isEnabled;
|
|
||||||
private FirebaseApp fbApp;
|
|
||||||
private FirebaseAuth fbAuth;
|
|
||||||
|
|
||||||
private PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
|
private PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
|
||||||
|
|
||||||
public GoogleFirebaseAuthenticator(boolean isEnabled) {
|
public GoogleFirebaseAuthenticator(boolean isEnabled, String credsPath, String db) {
|
||||||
this.isEnabled = isEnabled;
|
super(isEnabled, "AuthenticationProvider", credsPath, db);
|
||||||
}
|
|
||||||
|
|
||||||
public GoogleFirebaseAuthenticator(String credsPath, String db) {
|
|
||||||
this(true);
|
|
||||||
try {
|
|
||||||
fbApp = FirebaseApp.initializeApp(getOpts(credsPath, db), "AuthenticationProvider");
|
|
||||||
fbAuth = FirebaseAuth.getInstance(fbApp);
|
|
||||||
|
|
||||||
log.info("Google Firebase Authentication is ready");
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException("Error when initializing Firebase", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitOnLatch(BackendAuthResult result, CountDownLatch l, String purpose) {
|
private void waitOnLatch(BackendAuthResult result, CountDownLatch l, String purpose) {
|
||||||
@@ -105,30 +82,6 @@ public class GoogleFirebaseAuthenticator implements AuthenticatorProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private FirebaseCredential getCreds(String credsPath) throws IOException {
|
|
||||||
if (StringUtils.isNotBlank(credsPath)) {
|
|
||||||
return FirebaseCredentials.fromCertificate(new FileInputStream(credsPath));
|
|
||||||
} else {
|
|
||||||
return FirebaseCredentials.applicationDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private FirebaseOptions getOpts(String credsPath, String db) throws IOException {
|
|
||||||
if (StringUtils.isBlank(db)) {
|
|
||||||
throw new IllegalArgumentException("Firebase database is not configured");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FirebaseOptions.Builder()
|
|
||||||
.setCredential(getCreds(credsPath))
|
|
||||||
.setDatabaseUrl(db)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return isEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void waitOnLatch(CountDownLatch l) {
|
private void waitOnLatch(CountDownLatch l) {
|
||||||
try {
|
try {
|
||||||
l.await(30, TimeUnit.SECONDS);
|
l.await(30, TimeUnit.SECONDS);
|
||||||
@@ -149,7 +102,7 @@ public class GoogleFirebaseAuthenticator implements AuthenticatorProvider {
|
|||||||
|
|
||||||
String localpart = mxid.getLocalPart();
|
String localpart = mxid.getLocalPart();
|
||||||
CountDownLatch l = new CountDownLatch(1);
|
CountDownLatch l = new CountDownLatch(1);
|
||||||
fbAuth.verifyIdToken(password).addOnSuccessListener(token -> {
|
getFirebase().verifyIdToken(password).addOnSuccessListener(token -> {
|
||||||
try {
|
try {
|
||||||
if (!StringUtils.equals(localpart, token.getUid())) {
|
if (!StringUtils.equals(localpart, token.getUid())) {
|
||||||
log.info("Failure to authenticate {}: Matrix ID localpart '{}' does not match Firebase UID '{}'", mxid, localpart, token.getUid());
|
log.info("Failure to authenticate {}: Matrix ID localpart '{}' does not match Firebase UID '{}'", mxid, localpart, token.getUid());
|
||||||
@@ -161,7 +114,7 @@ public class GoogleFirebaseAuthenticator implements AuthenticatorProvider {
|
|||||||
log.info("{} was successfully authenticated", mxid);
|
log.info("{} was successfully authenticated", mxid);
|
||||||
log.info("Fetching profile for {}", mxid);
|
log.info("Fetching profile for {}", mxid);
|
||||||
CountDownLatch userRecordLatch = new CountDownLatch(1);
|
CountDownLatch userRecordLatch = new CountDownLatch(1);
|
||||||
fbAuth.getUser(token.getUid()).addOnSuccessListener(user -> {
|
getFirebase().getUser(token.getUid()).addOnSuccessListener(user -> {
|
||||||
try {
|
try {
|
||||||
toEmail(result, user.getEmail());
|
toEmail(result, user.getEmail());
|
||||||
toMsisdn(result, user.getPhoneNumber());
|
toMsisdn(result, user.getPhoneNumber());
|
||||||
|
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* mxisd - Matrix Identity Server Daemon
|
||||||
|
* Copyright (C) 2017 Maxime Dor
|
||||||
|
*
|
||||||
|
* https://max.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.backend.firebase;
|
||||||
|
|
||||||
|
import com.google.firebase.FirebaseApp;
|
||||||
|
import com.google.firebase.FirebaseOptions;
|
||||||
|
import com.google.firebase.auth.FirebaseAuth;
|
||||||
|
import com.google.firebase.auth.FirebaseCredential;
|
||||||
|
import com.google.firebase.auth.FirebaseCredentials;
|
||||||
|
import com.google.firebase.database.FirebaseDatabase;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class GoogleFirebaseBackend {
|
||||||
|
|
||||||
|
private Logger log = LoggerFactory.getLogger(GoogleFirebaseBackend.class);
|
||||||
|
|
||||||
|
private boolean isEnabled;
|
||||||
|
private FirebaseAuth fbAuth;
|
||||||
|
protected FirebaseDatabase fbDb;
|
||||||
|
|
||||||
|
GoogleFirebaseBackend(boolean isEnabled, String name, String credsPath, String db) {
|
||||||
|
this.isEnabled = isEnabled;
|
||||||
|
try {
|
||||||
|
FirebaseApp fbApp = FirebaseApp.initializeApp(getOpts(credsPath, db), name);
|
||||||
|
fbAuth = FirebaseAuth.getInstance(fbApp);
|
||||||
|
FirebaseDatabase.getInstance(fbApp);
|
||||||
|
|
||||||
|
log.info("Google Firebase Authentication is ready");
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Error when initializing Firebase", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private FirebaseCredential getCreds(String credsPath) throws IOException {
|
||||||
|
if (StringUtils.isNotBlank(credsPath)) {
|
||||||
|
return FirebaseCredentials.fromCertificate(new FileInputStream(credsPath));
|
||||||
|
} else {
|
||||||
|
return FirebaseCredentials.applicationDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private FirebaseOptions getOpts(String credsPath, String db) throws IOException {
|
||||||
|
if (StringUtils.isBlank(db)) {
|
||||||
|
throw new IllegalArgumentException("Firebase database is not configured");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FirebaseOptions.Builder()
|
||||||
|
.setCredential(getCreds(credsPath))
|
||||||
|
.setDatabaseUrl(db)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
FirebaseAuth getFirebase() {
|
||||||
|
return fbAuth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return isEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -20,11 +20,6 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.backend.firebase;
|
package io.kamax.mxisd.backend.firebase;
|
||||||
|
|
||||||
import com.google.firebase.FirebaseApp;
|
|
||||||
import com.google.firebase.FirebaseOptions;
|
|
||||||
import com.google.firebase.auth.FirebaseAuth;
|
|
||||||
import com.google.firebase.auth.FirebaseCredential;
|
|
||||||
import com.google.firebase.auth.FirebaseCredentials;
|
|
||||||
import com.google.firebase.auth.UserRecord;
|
import com.google.firebase.auth.UserRecord;
|
||||||
import com.google.firebase.tasks.OnFailureListener;
|
import com.google.firebase.tasks.OnFailureListener;
|
||||||
import com.google.firebase.tasks.OnSuccessListener;
|
import com.google.firebase.tasks.OnSuccessListener;
|
||||||
@@ -34,72 +29,29 @@ import io.kamax.mxisd.lookup.SingleLookupReply;
|
|||||||
import io.kamax.mxisd.lookup.SingleLookupRequest;
|
import io.kamax.mxisd.lookup.SingleLookupRequest;
|
||||||
import io.kamax.mxisd.lookup.ThreePidMapping;
|
import io.kamax.mxisd.lookup.ThreePidMapping;
|
||||||
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
|
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
|
||||||
import org.apache.commons.lang.StringUtils;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class GoogleFirebaseProvider implements IThreePidProvider {
|
public class GoogleFirebaseProvider extends GoogleFirebaseBackend implements IThreePidProvider {
|
||||||
|
|
||||||
private Logger log = LoggerFactory.getLogger(GoogleFirebaseProvider.class);
|
private Logger log = LoggerFactory.getLogger(GoogleFirebaseProvider.class);
|
||||||
|
|
||||||
private boolean isEnabled;
|
|
||||||
private String domain;
|
private String domain;
|
||||||
private FirebaseAuth fbAuth;
|
|
||||||
|
|
||||||
public GoogleFirebaseProvider(boolean isEnabled) {
|
public GoogleFirebaseProvider(boolean isEnabled, String credsPath, String db, String domain) {
|
||||||
this.isEnabled = isEnabled;
|
super(isEnabled, "ThreePidProvider", credsPath, db);
|
||||||
}
|
|
||||||
|
|
||||||
public GoogleFirebaseProvider(String credsPath, String db, String domain) {
|
|
||||||
this(true);
|
|
||||||
this.domain = domain;
|
this.domain = domain;
|
||||||
|
|
||||||
try {
|
|
||||||
FirebaseApp fbApp = FirebaseApp.initializeApp(getOpts(credsPath, db), "ThreePidProvider");
|
|
||||||
fbAuth = FirebaseAuth.getInstance(fbApp);
|
|
||||||
|
|
||||||
log.info("Google Firebase Authentication is ready");
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException("Error when initializing Firebase", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private FirebaseCredential getCreds(String credsPath) throws IOException {
|
|
||||||
if (StringUtils.isNotBlank(credsPath)) {
|
|
||||||
return FirebaseCredentials.fromCertificate(new FileInputStream(credsPath));
|
|
||||||
} else {
|
|
||||||
return FirebaseCredentials.applicationDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private FirebaseOptions getOpts(String credsPath, String db) throws IOException {
|
|
||||||
if (StringUtils.isBlank(db)) {
|
|
||||||
throw new IllegalArgumentException("Firebase database is not configured");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FirebaseOptions.Builder()
|
|
||||||
.setCredential(getCreds(credsPath))
|
|
||||||
.setDatabaseUrl(db)
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getMxid(UserRecord record) {
|
private String getMxid(UserRecord record) {
|
||||||
return new MatrixID(record.getUid(), domain).getId();
|
return new MatrixID(record.getUid(), domain).getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return isEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isLocal() {
|
public boolean isLocal() {
|
||||||
return true;
|
return true;
|
||||||
@@ -136,13 +88,13 @@ public class GoogleFirebaseProvider implements IThreePidProvider {
|
|||||||
|
|
||||||
if (ThreePidMedium.Email.is(medium)) {
|
if (ThreePidMedium.Email.is(medium)) {
|
||||||
log.info("Performing E-mail 3PID lookup for {}", address);
|
log.info("Performing E-mail 3PID lookup for {}", address);
|
||||||
fbAuth.getUserByEmail(address)
|
getFirebase().getUserByEmail(address)
|
||||||
.addOnSuccessListener(success)
|
.addOnSuccessListener(success)
|
||||||
.addOnFailureListener(failure);
|
.addOnFailureListener(failure);
|
||||||
waitOnLatch(l);
|
waitOnLatch(l);
|
||||||
} else if (ThreePidMedium.PhoneNumber.is(medium)) {
|
} else if (ThreePidMedium.PhoneNumber.is(medium)) {
|
||||||
log.info("Performing msisdn 3PID lookup for {}", address);
|
log.info("Performing msisdn 3PID lookup for {}", address);
|
||||||
fbAuth.getUserByPhoneNumber(address)
|
getFirebase().getUserByPhoneNumber(address)
|
||||||
.addOnSuccessListener(success)
|
.addOnSuccessListener(success)
|
||||||
.addOnFailureListener(failure);
|
.addOnFailureListener(failure);
|
||||||
waitOnLatch(l);
|
waitOnLatch(l);
|
||||||
|
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* mxisd - Matrix Identity Server Daemon
|
||||||
|
* Copyright (C) 2017 Maxime Dor
|
||||||
|
*
|
||||||
|
* https://max.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.backend.sql;
|
||||||
|
|
||||||
|
import com.mchange.v2.c3p0.ComboPooledDataSource;
|
||||||
|
import io.kamax.mxisd.config.sql.SqlProviderConfig;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class SqlConnectionPool {
|
||||||
|
|
||||||
|
private ComboPooledDataSource ds;
|
||||||
|
|
||||||
|
public SqlConnectionPool(SqlProviderConfig cfg) {
|
||||||
|
ds = new ComboPooledDataSource();
|
||||||
|
ds.setJdbcUrl("jdbc:" + cfg.getType() + ":" + cfg.getConnection());
|
||||||
|
ds.setMinPoolSize(1);
|
||||||
|
ds.setMaxPoolSize(10);
|
||||||
|
ds.setAcquireIncrement(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Connection get() throws SQLException {
|
||||||
|
return ds.getConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* mxisd - Matrix Identity Server Daemon
|
||||||
|
* Copyright (C) 2017 Maxime Dor
|
||||||
|
*
|
||||||
|
* https://max.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.backend.sql;
|
||||||
|
|
||||||
|
import io.kamax.mxisd.config.MatrixConfig;
|
||||||
|
import io.kamax.mxisd.config.sql.SqlProviderConfig;
|
||||||
|
import io.kamax.mxisd.controller.directory.io.UserDirectorySearchResult;
|
||||||
|
import io.kamax.mxisd.directory.IDirectoryProvider;
|
||||||
|
import io.kamax.mxisd.exception.InternalServerError;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static io.kamax.mxisd.controller.directory.io.UserDirectorySearchResult.Result;
|
||||||
|
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class SqlDirectoryProvider implements IDirectoryProvider {
|
||||||
|
|
||||||
|
private Logger log = LoggerFactory.getLogger(SqlDirectoryProvider.class);
|
||||||
|
|
||||||
|
protected SqlProviderConfig cfg;
|
||||||
|
private MatrixConfig mxCfg;
|
||||||
|
|
||||||
|
private SqlConnectionPool pool;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public SqlDirectoryProvider(SqlProviderConfig cfg, MatrixConfig mxCfg, SqlConnectionPool pool) {
|
||||||
|
this.cfg = cfg;
|
||||||
|
this.pool = pool;
|
||||||
|
this.mxCfg = mxCfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return cfg.isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setParameters(PreparedStatement stmt, String searchTerm) throws SQLException {
|
||||||
|
for (int i = 1; i <= stmt.getParameterMetaData().getParameterCount(); i++) {
|
||||||
|
stmt.setString(i, searchTerm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Optional<Result> processRow(ResultSet rSet) throws SQLException {
|
||||||
|
Result item = new Result();
|
||||||
|
item.setUserId(rSet.getString(1));
|
||||||
|
item.setDisplayName(rSet.getString(2));
|
||||||
|
return Optional.of(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserDirectorySearchResult search(String searchTerm, SqlProviderConfig.Query query) {
|
||||||
|
try (Connection conn = pool.get()) {
|
||||||
|
try (PreparedStatement stmt = conn.prepareStatement(query.getValue())) {
|
||||||
|
setParameters(stmt, searchTerm);
|
||||||
|
|
||||||
|
try (ResultSet rSet = stmt.executeQuery()) {
|
||||||
|
UserDirectorySearchResult result = new UserDirectorySearchResult();
|
||||||
|
result.setLimited(false);
|
||||||
|
|
||||||
|
while (rSet.next()) {
|
||||||
|
processRow(rSet).ifPresent(e -> {
|
||||||
|
if (StringUtils.equalsIgnoreCase("localpart", query.getType())) {
|
||||||
|
e.setUserId("@" + e.getUserId() + mxCfg.getDomain());
|
||||||
|
}
|
||||||
|
result.addResult(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new InternalServerError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDirectorySearchResult searchByDisplayName(String searchTerm) {
|
||||||
|
log.info("Searching users by display name using '{}'", searchTerm);
|
||||||
|
return search(searchTerm, cfg.getDirectory().getQuery().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDirectorySearchResult searchBy3pid(String searchTerm) {
|
||||||
|
log.info("Searching users by 3PID using '{}'", searchTerm);
|
||||||
|
return search(searchTerm, cfg.getDirectory().getQuery().getThreepid());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -33,7 +33,10 @@ import org.slf4j.LoggerFactory;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.sql.*;
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -43,11 +46,17 @@ public class SqlThreePidProvider implements IThreePidProvider {
|
|||||||
|
|
||||||
private Logger log = LoggerFactory.getLogger(SqlThreePidProvider.class);
|
private Logger log = LoggerFactory.getLogger(SqlThreePidProvider.class);
|
||||||
|
|
||||||
@Autowired
|
private SqlProviderConfig cfg;
|
||||||
private MatrixConfig mxCfg;
|
private MatrixConfig mxCfg;
|
||||||
|
|
||||||
|
private SqlConnectionPool pool;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private SqlProviderConfig cfg;
|
public SqlThreePidProvider(SqlProviderConfig cfg, MatrixConfig mxCfg, SqlConnectionPool pool) {
|
||||||
|
this.cfg = cfg;
|
||||||
|
this.pool = pool;
|
||||||
|
this.mxCfg = mxCfg;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
@@ -64,20 +73,17 @@ public class SqlThreePidProvider implements IThreePidProvider {
|
|||||||
return 20;
|
return 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Connection getConn() throws SQLException {
|
|
||||||
return DriverManager.getConnection("jdbc:" + cfg.getType() + ":" + cfg.getConnection());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<SingleLookupReply> find(SingleLookupRequest request) {
|
public Optional<SingleLookupReply> find(SingleLookupRequest request) {
|
||||||
log.info("SQL lookup");
|
log.info("SQL lookup");
|
||||||
String stmtSql = StringUtils.defaultIfBlank(cfg.getIdentity().getMedium().get(request.getType()), cfg.getIdentity().getQuery());
|
String stmtSql = StringUtils.defaultIfBlank(cfg.getIdentity().getMedium().get(request.getType()), cfg.getIdentity().getQuery());
|
||||||
log.info("SQL query: {}", stmtSql);
|
log.info("SQL query: {}", stmtSql);
|
||||||
try (PreparedStatement stmt = getConn().prepareStatement(stmtSql)) {
|
try (Connection conn = pool.get()) {
|
||||||
|
try (PreparedStatement stmt = conn.prepareStatement(stmtSql)) {
|
||||||
stmt.setString(1, request.getType().toLowerCase());
|
stmt.setString(1, request.getType().toLowerCase());
|
||||||
stmt.setString(2, request.getThreePid().toLowerCase());
|
stmt.setString(2, request.getThreePid().toLowerCase());
|
||||||
|
|
||||||
ResultSet rSet = stmt.executeQuery();
|
try (ResultSet rSet = stmt.executeQuery()) {
|
||||||
while (rSet.next()) {
|
while (rSet.next()) {
|
||||||
String uid = rSet.getString("uid");
|
String uid = rSet.getString("uid");
|
||||||
log.info("Found match: {}", uid);
|
log.info("Found match: {}", uid);
|
||||||
@@ -95,6 +101,8 @@ public class SqlThreePidProvider implements IThreePidProvider {
|
|||||||
|
|
||||||
log.info("No match found in SQL");
|
log.info("No match found in SQL");
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* mxisd - Matrix Identity Server Daemon
|
||||||
|
* Copyright (C) 2017 Maxime Dor
|
||||||
|
*
|
||||||
|
* https://max.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.backend.sql;
|
||||||
|
|
||||||
|
import io.kamax.mxisd.config.MatrixConfig;
|
||||||
|
import io.kamax.mxisd.config.sql.SqlProviderConfig;
|
||||||
|
import io.kamax.mxisd.config.sql.synapse.SynapseSqlProviderConfig;
|
||||||
|
import io.kamax.mxisd.exception.ConfigurationException;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class SynapseSqliteDirectoryProvider extends SqlDirectoryProvider {
|
||||||
|
|
||||||
|
private SynapseSqlProviderConfig cfg;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public SynapseSqliteDirectoryProvider(SynapseSqlProviderConfig cfg, MatrixConfig mxCfg, SqlConnectionPool pool) {
|
||||||
|
super(cfg, mxCfg, pool);
|
||||||
|
|
||||||
|
if (StringUtils.equals("sqlite", cfg.getType())) {
|
||||||
|
String userId = "'@' || p.user_id || ':" + mxCfg.getDomain() + "'";
|
||||||
|
SqlProviderConfig.Type queries = cfg.getDirectory().getQuery();
|
||||||
|
queries.getName().setValue(
|
||||||
|
"select " + userId + ", displayname from profiles p where displayname like ?");
|
||||||
|
queries.getThreepid().setValue(
|
||||||
|
"select t.user_id, p.displayname " +
|
||||||
|
"from user_threepids t JOIN profiles p on t.user_id = " + userId + " " +
|
||||||
|
"where t.address like ?");
|
||||||
|
} else if (StringUtils.equals("postgresql", cfg.getType())) {
|
||||||
|
String userId = "concat('@',p.user_id,':" + mxCfg.getDomain() + "')";
|
||||||
|
SqlProviderConfig.Type queries = cfg.getDirectory().getQuery();
|
||||||
|
queries.getName().setValue(
|
||||||
|
"select " + userId + ", displayname from profiles p where displayname ilike ?");
|
||||||
|
queries.getThreepid().setValue(
|
||||||
|
"select t.user_id, p.displayname " +
|
||||||
|
"from user_threepids t JOIN profiles p on t.user_id = " + userId + " " +
|
||||||
|
"where t.address ilike ?");
|
||||||
|
} else {
|
||||||
|
throw new ConfigurationException("Invalid SQL type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setParameters(PreparedStatement stmt, String searchTerm) throws SQLException {
|
||||||
|
stmt.setString(1, "%" + searchTerm + "%");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
106
src/main/java/io/kamax/mxisd/config/DnsOverwriteConfig.java
Normal file
106
src/main/java/io/kamax/mxisd/config/DnsOverwriteConfig.java
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* mxisd - Matrix Identity Server Daemon
|
||||||
|
* Copyright (C) 2017 Maxime Dor
|
||||||
|
*
|
||||||
|
* https://max.kamax.io/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.kamax.mxisd.config;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import io.kamax.mxisd.util.GsonUtil;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties("dns.overwrite")
|
||||||
|
public class DnsOverwriteConfig {
|
||||||
|
|
||||||
|
private Logger log = LoggerFactory.getLogger(DnsOverwriteConfig.class);
|
||||||
|
|
||||||
|
public static class Entry {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Type {
|
||||||
|
|
||||||
|
List<Entry> client = new ArrayList<>();
|
||||||
|
List<Entry> federation = new ArrayList<>();
|
||||||
|
|
||||||
|
public List<Entry> getClient() {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClient(List<Entry> client) {
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Entry> getFederation() {
|
||||||
|
return federation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFederation(List<Entry> federation) {
|
||||||
|
this.federation = federation;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Type homeserver = new Type();
|
||||||
|
|
||||||
|
public Type getHomeserver() {
|
||||||
|
return homeserver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHomeserver(Type homeserver) {
|
||||||
|
this.homeserver = homeserver;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void build() {
|
||||||
|
Gson gson = GsonUtil.build();
|
||||||
|
log.info("--- DNS Overwrite config ---");
|
||||||
|
log.info("Homeserver:");
|
||||||
|
log.info("\tClient: {}", gson.toJson(getHomeserver().getClient()));
|
||||||
|
log.info("\tFederation: {}", gson.toJson(getHomeserver().getFederation()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* mxisd - Matrix Identity Server Daemon
|
|
||||||
* Copyright (C) 2017 Maxime Dor
|
|
||||||
*
|
|
||||||
* https://max.kamax.io/
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.kamax.mxisd.config;
|
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@ConfigurationProperties("dns.overwrite.homeserver")
|
|
||||||
public class DnsOverwriteEntry {
|
|
||||||
|
|
||||||
private String name;
|
|
||||||
private String type;
|
|
||||||
private String value;
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setType(String type) {
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(String value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTarget() {
|
|
||||||
if (StringUtils.equals("env", getType())) {
|
|
||||||
return System.getenv(getValue());
|
|
||||||
} else {
|
|
||||||
return getValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -71,7 +71,7 @@ public class FirebaseConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
private void postConstruct() {
|
public void build() {
|
||||||
log.info("--- Firebase configuration ---");
|
log.info("--- Firebase configuration ---");
|
||||||
log.info("Enabled: {}", isEnabled());
|
log.info("Enabled: {}", isEnabled());
|
||||||
if (isEnabled()) {
|
if (isEnabled()) {
|
||||||
@@ -82,20 +82,12 @@ public class FirebaseConfig {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public AuthenticatorProvider getAuthProvider() {
|
public AuthenticatorProvider getAuthProvider() {
|
||||||
if (!enabled) {
|
return new GoogleFirebaseAuthenticator(enabled, credentials, database);
|
||||||
return new GoogleFirebaseAuthenticator(false);
|
|
||||||
} else {
|
|
||||||
return new GoogleFirebaseAuthenticator(credentials, database);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public IThreePidProvider getLookupProvider() {
|
public IThreePidProvider getLookupProvider() {
|
||||||
if (!enabled) {
|
return new GoogleFirebaseProvider(enabled, credentials, database, mxCfg.getDomain());
|
||||||
return new GoogleFirebaseProvider(false);
|
|
||||||
} else {
|
|
||||||
return new GoogleFirebaseProvider(credentials, database, mxCfg.getDomain());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,21 +0,0 @@
|
|||||||
package io.kamax.mxisd.config.sql;
|
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
|
|
||||||
// Unused
|
|
||||||
@Configuration
|
|
||||||
@ConfigurationProperties("sql.auth")
|
|
||||||
public class SqlProviderAuthConfig {
|
|
||||||
|
|
||||||
private boolean enabled;
|
|
||||||
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEnabled(boolean enabled) {
|
|
||||||
this.enabled = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -25,20 +25,149 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConfigurationProperties("sql")
|
@ConfigurationProperties("sql")
|
||||||
|
@Primary
|
||||||
public class SqlProviderConfig {
|
public class SqlProviderConfig {
|
||||||
|
|
||||||
private Logger log = LoggerFactory.getLogger(SqlProviderConfig.class);
|
private Logger log = LoggerFactory.getLogger(SqlProviderConfig.class);
|
||||||
|
|
||||||
|
public static class Query {
|
||||||
|
|
||||||
|
private String type;
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Type {
|
||||||
|
|
||||||
|
private Query name = new Query();
|
||||||
|
private Query threepid = new Query();
|
||||||
|
|
||||||
|
public Query getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(Query name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Query getThreepid() {
|
||||||
|
return threepid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setThreepid(Query threepid) {
|
||||||
|
this.threepid = threepid;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Auth {
|
||||||
|
|
||||||
|
private Boolean enabled;
|
||||||
|
|
||||||
|
public Boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(Boolean enabled) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Directory {
|
||||||
|
|
||||||
|
private Boolean enabled;
|
||||||
|
private Type query = new Type();
|
||||||
|
|
||||||
|
public Boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(Boolean enabled) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type getQuery() {
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQuery(Type query) {
|
||||||
|
this.query = query;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Identity {
|
||||||
|
|
||||||
|
private Boolean enabled;
|
||||||
|
private String type;
|
||||||
|
private String query;
|
||||||
|
private Map<String, String> medium = new HashMap<>();
|
||||||
|
|
||||||
|
public Boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(Boolean enabled) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getQuery() {
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQuery(String query) {
|
||||||
|
this.query = query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getMedium() {
|
||||||
|
return medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMedium(Map<String, String> medium) {
|
||||||
|
this.medium = medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
private String type;
|
private String type;
|
||||||
private String connection;
|
private String connection;
|
||||||
private SqlProviderAuthConfig auth;
|
private Auth auth = new Auth();
|
||||||
private SqlProviderIdentityConfig identity;
|
private Directory directory = new Directory();
|
||||||
|
private Identity identity = new Identity();
|
||||||
|
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return enabled;
|
return enabled;
|
||||||
@@ -64,31 +193,52 @@ public class SqlProviderConfig {
|
|||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SqlProviderAuthConfig getAuth() {
|
public Auth getAuth() {
|
||||||
return auth;
|
return auth;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAuth(SqlProviderAuthConfig auth) {
|
public void setAuth(Auth auth) {
|
||||||
this.auth = auth;
|
this.auth = auth;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SqlProviderIdentityConfig getIdentity() {
|
public Directory getDirectory() {
|
||||||
|
return directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDirectory(Directory directory) {
|
||||||
|
this.directory = directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Identity getIdentity() {
|
||||||
return identity;
|
return identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setIdentity(SqlProviderIdentityConfig identity) {
|
public void setIdentity(Identity identity) {
|
||||||
this.identity = identity;
|
this.identity = identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
private void postConstruct() {
|
public void build() {
|
||||||
log.info("--- SQL Provider config ---");
|
log.info("--- SQL Provider config ---");
|
||||||
|
|
||||||
|
if (getAuth().isEnabled() == null) {
|
||||||
|
getAuth().setEnabled(isEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getDirectory().isEnabled() == null) {
|
||||||
|
getDirectory().setEnabled(isEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getIdentity().isEnabled() == null) {
|
||||||
|
getIdentity().setEnabled(isEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
log.info("Enabled: {}", isEnabled());
|
log.info("Enabled: {}", isEnabled());
|
||||||
if (isEnabled()) {
|
if (isEnabled()) {
|
||||||
log.info("Type: {}", getType());
|
log.info("Type: {}", getType());
|
||||||
log.info("Connection: {}", getConnection());
|
log.info("Connection: {}", getConnection());
|
||||||
log.info("Auth enabled: {}", getAuth().isEnabled());
|
log.info("Auth enabled: {}", getAuth().isEnabled());
|
||||||
log.info("Identy type: {}", getIdentity().getType());
|
log.info("Identity type: {}", getIdentity().getType());
|
||||||
log.info("Identity medium queries: {}", new Gson().toJson(getIdentity().getMedium()));
|
log.info("Identity medium queries: {}", new Gson().toJson(getIdentity().getMedium()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,61 +0,0 @@
|
|||||||
/*
|
|
||||||
* mxisd - Matrix Identity Server Daemon
|
|
||||||
* Copyright (C) 2017 Maxime Dor
|
|
||||||
*
|
|
||||||
* https://max.kamax.io/
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.kamax.mxisd.config.sql;
|
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@ConfigurationProperties("sql.identity")
|
|
||||||
public class SqlProviderIdentityConfig {
|
|
||||||
|
|
||||||
private String type;
|
|
||||||
private String query;
|
|
||||||
private Map<String, String> medium = new HashMap<>();
|
|
||||||
|
|
||||||
public String getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setType(String type) {
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getQuery() {
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setQuery(String query) {
|
|
||||||
this.query = query;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, String> getMedium() {
|
|
||||||
return medium;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMedium(Map<String, String> medium) {
|
|
||||||
this.medium = medium;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -18,35 +18,22 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.kamax.mxisd.config;
|
package io.kamax.mxisd.config.sql.synapse;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import io.kamax.mxisd.config.sql.SqlProviderConfig;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
import java.util.Optional;
|
import static io.kamax.mxisd.config.sql.synapse.SynapseSqlProviderConfig.NAMESPACE;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConfigurationProperties("dns.overwrite")
|
@ConfigurationProperties(NAMESPACE)
|
||||||
public class DnsOverwrite {
|
public class SynapseSqlProviderConfig extends SqlProviderConfig {
|
||||||
|
|
||||||
private Logger log = LoggerFactory.getLogger(DnsOverwrite.class);
|
public static final String NAMESPACE = "synapseSql";
|
||||||
|
|
||||||
@Autowired
|
private Logger log = LoggerFactory.getLogger(SynapseSqlProviderConfig.class);
|
||||||
private ServerConfig srvCfg;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private DnsOverwriteEntry homeserver;
|
|
||||||
|
|
||||||
public Optional<DnsOverwriteEntry> findHost(String lookup) {
|
|
||||||
if (homeserver != null && StringUtils.equalsIgnoreCase(lookup, homeserver.getName())) {
|
|
||||||
return Optional.of(homeserver);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@@ -18,10 +18,11 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.kamax.mxisd.controller.identity.v1;
|
package io.kamax.mxisd.controller;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonSyntaxException;
|
||||||
import io.kamax.mxisd.exception.*;
|
import io.kamax.mxisd.exception.*;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -72,7 +73,7 @@ public class DefaultExceptionHandler {
|
|||||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||||
@ExceptionHandler(MissingServletRequestParameterException.class)
|
@ExceptionHandler(MissingServletRequestParameterException.class)
|
||||||
public String handle(MissingServletRequestParameterException e) {
|
public String handle(MissingServletRequestParameterException e) {
|
||||||
return handle("M_INVALID_BODY", e.getMessage());
|
return handle("M_INCOMPLETE_REQUEST", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||||
@@ -81,6 +82,12 @@ public class DefaultExceptionHandler {
|
|||||||
return handle("M_INVALID_JSON", e.getMessage());
|
return handle("M_INVALID_JSON", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||||
|
@ExceptionHandler(JsonSyntaxException.class)
|
||||||
|
public String handle(JsonSyntaxException e) {
|
||||||
|
return handle("M_INVALID_JSON", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||||
@ExceptionHandler(JsonMemberNotFoundException.class)
|
@ExceptionHandler(JsonMemberNotFoundException.class)
|
||||||
public String handle(JsonMemberNotFoundException e) {
|
public String handle(JsonMemberNotFoundException e) {
|
||||||
@@ -107,7 +114,7 @@ public class DefaultExceptionHandler {
|
|||||||
"M_UNKNOWN",
|
"M_UNKNOWN",
|
||||||
StringUtils.defaultIfBlank(
|
StringUtils.defaultIfBlank(
|
||||||
e.getMessage(),
|
e.getMessage(),
|
||||||
"An internal server error occured. If this error persists, please contact support with reference #" +
|
"An internal server error occurred. If this error persists, please contact support with reference #" +
|
||||||
Instant.now().toEpochMilli()
|
Instant.now().toEpochMilli()
|
||||||
)
|
)
|
||||||
);
|
);
|
@@ -28,10 +28,7 @@ import io.kamax.mxisd.util.GsonParser;
|
|||||||
import io.kamax.mxisd.util.GsonUtil;
|
import io.kamax.mxisd.util.GsonUtil;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -48,7 +45,7 @@ public class UserDirectoryController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private DirectoryManager mgr;
|
private DirectoryManager mgr;
|
||||||
|
|
||||||
@RequestMapping(path = "/search")
|
@RequestMapping(path = "/search", method = RequestMethod.POST)
|
||||||
public String search(HttpServletRequest request, @RequestParam("access_token") String accessToken) throws IOException {
|
public String search(HttpServletRequest request, @RequestParam("access_token") String accessToken) throws IOException {
|
||||||
UserDirectorySearchRequest searchQuery = parser.parse(request, UserDirectorySearchRequest.class);
|
UserDirectorySearchRequest searchQuery = parser.parse(request, UserDirectorySearchRequest.class);
|
||||||
URI target = URI.create(request.getRequestURL().toString());
|
URI target = URI.create(request.getRequestURL().toString());
|
||||||
|
@@ -24,6 +24,10 @@ public class UserDirectorySearchRequest {
|
|||||||
|
|
||||||
private String searchTerm;
|
private String searchTerm;
|
||||||
|
|
||||||
|
public UserDirectorySearchRequest(String searchTerm) {
|
||||||
|
setSearchTerm(searchTerm);
|
||||||
|
}
|
||||||
|
|
||||||
public String getSearchTerm() {
|
public String getSearchTerm() {
|
||||||
return searchTerm;
|
return searchTerm;
|
||||||
}
|
}
|
||||||
|
@@ -20,17 +20,105 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.directory;
|
package io.kamax.mxisd.directory;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import io.kamax.matrix.MatrixErrorInfo;
|
||||||
|
import io.kamax.mxisd.controller.directory.io.UserDirectorySearchRequest;
|
||||||
import io.kamax.mxisd.controller.directory.io.UserDirectorySearchResult;
|
import io.kamax.mxisd.controller.directory.io.UserDirectorySearchResult;
|
||||||
import io.kamax.mxisd.exception.NotImplementedException;
|
import io.kamax.mxisd.dns.ClientDnsOverwrite;
|
||||||
|
import io.kamax.mxisd.exception.InternalServerError;
|
||||||
|
import io.kamax.mxisd.exception.MatrixException;
|
||||||
|
import io.kamax.mxisd.util.GsonUtil;
|
||||||
|
import io.kamax.mxisd.util.RestClientUtils;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.client.utils.URIBuilder;
|
||||||
|
import org.apache.http.entity.ContentType;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClients;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class DirectoryManager {
|
public class DirectoryManager {
|
||||||
|
|
||||||
|
private Logger log = LoggerFactory.getLogger(DirectoryManager.class);
|
||||||
|
|
||||||
|
private List<IDirectoryProvider> providers;
|
||||||
|
|
||||||
|
private ClientDnsOverwrite dns;
|
||||||
|
private CloseableHttpClient client;
|
||||||
|
private Gson gson;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public DirectoryManager(List<IDirectoryProvider> providers, ClientDnsOverwrite dns) {
|
||||||
|
this.dns = dns;
|
||||||
|
this.client = HttpClients.custom().setUserAgent("mxisd").build(); //FIXME centralize
|
||||||
|
this.gson = GsonUtil.build();
|
||||||
|
this.providers = providers.stream().filter(IDirectoryProvider::isEnabled).collect(Collectors.toList());
|
||||||
|
|
||||||
|
log.info("Directory providers:");
|
||||||
|
this.providers.forEach(p -> log.info("\t- {}", p.getClass().getName()));
|
||||||
|
}
|
||||||
|
|
||||||
public UserDirectorySearchResult search(URI target, String accessToken, String query) {
|
public UserDirectorySearchResult search(URI target, String accessToken, String query) {
|
||||||
throw new NotImplementedException("user directory - search");
|
log.info("Performing search for '{}'", query);
|
||||||
|
log.info("Original request URL: {}", target);
|
||||||
|
UserDirectorySearchResult result = new UserDirectorySearchResult();
|
||||||
|
|
||||||
|
URIBuilder builder = dns.transform(target);
|
||||||
|
log.info("Querying HS at {}", builder);
|
||||||
|
builder.setParameter("access_token", accessToken);
|
||||||
|
HttpPost req = RestClientUtils.post(
|
||||||
|
builder.toString(),
|
||||||
|
new UserDirectorySearchRequest(query));
|
||||||
|
try (CloseableHttpResponse res = client.execute(req)) {
|
||||||
|
int status = res.getStatusLine().getStatusCode();
|
||||||
|
Charset charset = ContentType.getOrDefault(res.getEntity()).getCharset();
|
||||||
|
String body = IOUtils.toString(res.getEntity().getContent(), charset);
|
||||||
|
|
||||||
|
if (status != 200) {
|
||||||
|
MatrixErrorInfo info = gson.fromJson(body, MatrixErrorInfo.class);
|
||||||
|
throw new MatrixException(status, info.getErrcode(), info.getError());
|
||||||
|
}
|
||||||
|
|
||||||
|
UserDirectorySearchResult resultHs = gson.fromJson(body, UserDirectorySearchResult.class);
|
||||||
|
log.info("Found {} match(es) in HS for '{}'", resultHs.getResults().size(), query);
|
||||||
|
result.getResults().addAll(resultHs.getResults());
|
||||||
|
if (resultHs.isLimited()) {
|
||||||
|
result.setLimited(true);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new InternalServerError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (IDirectoryProvider provider : providers) {
|
||||||
|
log.info("Using Directory provider {}", provider.getClass().getSimpleName());
|
||||||
|
UserDirectorySearchResult resultProvider = provider.searchByDisplayName(query);
|
||||||
|
log.info("Display name: found {} match(es) for '{}'", resultProvider.getResults().size(), query);
|
||||||
|
result.getResults().addAll(resultProvider.getResults());
|
||||||
|
if (resultProvider.isLimited()) {
|
||||||
|
result.setLimited(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
resultProvider = provider.searchBy3pid(query);
|
||||||
|
log.info("Threepid: found {} match(es) for '{}'", resultProvider.getResults().size(), query);
|
||||||
|
result.getResults().addAll(resultProvider.getResults());
|
||||||
|
if (resultProvider.isLimited()) {
|
||||||
|
result.setLimited(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Total matches: {} - limited? {}", result.getResults().size(), result.isLimited());
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* mxisd - Matrix Identity Server Daemon
|
||||||
|
* Copyright (C) 2017 Maxime Dor
|
||||||
|
*
|
||||||
|
* https://max.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.directory;
|
||||||
|
|
||||||
|
import io.kamax.mxisd.controller.directory.io.UserDirectorySearchResult;
|
||||||
|
|
||||||
|
public interface IDirectoryProvider {
|
||||||
|
|
||||||
|
boolean isEnabled();
|
||||||
|
|
||||||
|
UserDirectorySearchResult searchByDisplayName(String query);
|
||||||
|
|
||||||
|
UserDirectorySearchResult searchBy3pid(String query);
|
||||||
|
|
||||||
|
}
|
74
src/main/java/io/kamax/mxisd/dns/ClientDnsOverwrite.java
Normal file
74
src/main/java/io/kamax/mxisd/dns/ClientDnsOverwrite.java
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* mxisd - Matrix Identity Server Daemon
|
||||||
|
* Copyright (C) 2017 Maxime Dor
|
||||||
|
*
|
||||||
|
* https://max.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.dns;
|
||||||
|
|
||||||
|
import io.kamax.mxisd.config.DnsOverwriteConfig;
|
||||||
|
import io.kamax.mxisd.exception.ConfigurationException;
|
||||||
|
import org.apache.http.client.utils.URIBuilder;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static io.kamax.mxisd.config.DnsOverwriteConfig.Entry;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class ClientDnsOverwrite {
|
||||||
|
|
||||||
|
private Logger log = LoggerFactory.getLogger(ClientDnsOverwrite.class);
|
||||||
|
|
||||||
|
private Map<String, Entry> mappings;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public ClientDnsOverwrite(DnsOverwriteConfig cfg) {
|
||||||
|
mappings = new HashMap<>();
|
||||||
|
cfg.getHomeserver().getClient().forEach(e -> mappings.put(e.getName(), e));
|
||||||
|
}
|
||||||
|
|
||||||
|
public URIBuilder transform(URI initial) {
|
||||||
|
URIBuilder builder = new URIBuilder(initial);
|
||||||
|
Entry mapping = mappings.get(initial.getHost());
|
||||||
|
if (mapping == null) {
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
URL target = new URL(mapping.getValue());
|
||||||
|
builder.setScheme(target.getProtocol());
|
||||||
|
builder.setHost(target.getHost());
|
||||||
|
if (target.getPort() != -1) {
|
||||||
|
builder.setPort(target.getPort());
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
log.warn("Skipping DNS overwrite entry {} due to invalid value [{}]: {}", mapping.getName(), mapping.getValue(), e.getMessage());
|
||||||
|
throw new ConfigurationException("Invalid DNS overwrite entry in homeserver client: " + mapping.getName(), e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
61
src/main/java/io/kamax/mxisd/dns/FederationDnsOverwrite.java
Normal file
61
src/main/java/io/kamax/mxisd/dns/FederationDnsOverwrite.java
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* mxisd - Matrix Identity Server Daemon
|
||||||
|
* Copyright (C) 2017 Maxime Dor
|
||||||
|
*
|
||||||
|
* https://max.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.dns;
|
||||||
|
|
||||||
|
import io.kamax.mxisd.config.DnsOverwriteConfig;
|
||||||
|
import io.kamax.mxisd.config.ServerConfig;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static io.kamax.mxisd.config.DnsOverwriteConfig.Entry;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class FederationDnsOverwrite {
|
||||||
|
|
||||||
|
private Logger log = LoggerFactory.getLogger(FederationDnsOverwrite.class);
|
||||||
|
|
||||||
|
private ServerConfig srvCfg;
|
||||||
|
private Map<String, Entry> mappings;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public FederationDnsOverwrite(DnsOverwriteConfig cfg, ServerConfig srvCfg) {
|
||||||
|
this.srvCfg = srvCfg;
|
||||||
|
|
||||||
|
mappings = new HashMap<>();
|
||||||
|
cfg.getHomeserver().getFederation().forEach(e -> mappings.put(e.getName(), e));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> findHost(String lookup) {
|
||||||
|
Entry mapping = mappings.get(lookup);
|
||||||
|
if (mapping == null) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.of(mapping.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.exception;
|
package io.kamax.mxisd.exception;
|
||||||
|
|
||||||
public abstract class MatrixException extends MxisdException {
|
public class MatrixException extends MxisdException {
|
||||||
|
|
||||||
private int status;
|
private int status;
|
||||||
private String errorCode;
|
private String errorCode;
|
||||||
|
@@ -0,0 +1,11 @@
|
|||||||
|
package io.kamax.mxisd.exception;
|
||||||
|
|
||||||
|
import org.apache.http.HttpStatus;
|
||||||
|
|
||||||
|
public class RemoteHomeServerException extends MatrixException {
|
||||||
|
|
||||||
|
public RemoteHomeServerException(String error) {
|
||||||
|
super(HttpStatus.SC_SERVICE_UNAVAILABLE, "M_REMOTE_HS_ERROR", "Error from remote server: " + error);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -24,8 +24,7 @@ import com.google.gson.Gson;
|
|||||||
import com.google.gson.JsonArray;
|
import com.google.gson.JsonArray;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import io.kamax.matrix.MatrixID;
|
import io.kamax.matrix.MatrixID;
|
||||||
import io.kamax.mxisd.config.DnsOverwrite;
|
import io.kamax.mxisd.dns.FederationDnsOverwrite;
|
||||||
import io.kamax.mxisd.config.DnsOverwriteEntry;
|
|
||||||
import io.kamax.mxisd.exception.BadRequestException;
|
import io.kamax.mxisd.exception.BadRequestException;
|
||||||
import io.kamax.mxisd.exception.MappingAlreadyExistsException;
|
import io.kamax.mxisd.exception.MappingAlreadyExistsException;
|
||||||
import io.kamax.mxisd.lookup.SingleLookupReply;
|
import io.kamax.mxisd.lookup.SingleLookupReply;
|
||||||
@@ -81,7 +80,7 @@ public class InvitationManager {
|
|||||||
private SignatureManager signMgr;
|
private SignatureManager signMgr;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private DnsOverwrite dns;
|
private FederationDnsOverwrite dns;
|
||||||
|
|
||||||
private NotificationManager notifMgr;
|
private NotificationManager notifMgr;
|
||||||
|
|
||||||
@@ -160,11 +159,11 @@ public class InvitationManager {
|
|||||||
// TODO use caching mechanism
|
// TODO use caching mechanism
|
||||||
// TODO export in matrix-java-sdk
|
// TODO export in matrix-java-sdk
|
||||||
private String findHomeserverForDomain(String domain) {
|
private String findHomeserverForDomain(String domain) {
|
||||||
Optional<DnsOverwriteEntry> entryOpt = dns.findHost(domain);
|
Optional<String> entryOpt = dns.findHost(domain);
|
||||||
if (entryOpt.isPresent()) {
|
if (entryOpt.isPresent()) {
|
||||||
DnsOverwriteEntry entry = entryOpt.get();
|
String entry = entryOpt.get();
|
||||||
log.info("Found DNS overwrite for {} to {}", entry.getName(), entry.getTarget());
|
log.info("Found DNS overwrite for {} to {}", domain, entry);
|
||||||
return "https://" + entry.getTarget();
|
return "https://" + entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug("Performing SRV lookup for {}", domain);
|
log.debug("Performing SRV lookup for {}", domain);
|
||||||
|
@@ -43,26 +43,28 @@ public class RecursivePriorityLookupStrategy implements LookupStrategy {
|
|||||||
|
|
||||||
private Logger log = LoggerFactory.getLogger(RecursivePriorityLookupStrategy.class);
|
private Logger log = LoggerFactory.getLogger(RecursivePriorityLookupStrategy.class);
|
||||||
|
|
||||||
@Autowired
|
private RecursiveLookupConfig cfg;
|
||||||
private RecursiveLookupConfig recursiveCfg;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private List<IThreePidProvider> providers;
|
private List<IThreePidProvider> providers;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private IBridgeFetcher bridge;
|
private IBridgeFetcher bridge;
|
||||||
|
|
||||||
private List<CIDRUtils> allowedCidr = new ArrayList<>();
|
private List<CIDRUtils> allowedCidr = new ArrayList<>();
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public RecursivePriorityLookupStrategy(RecursiveLookupConfig cfg, List<IThreePidProvider> providers, IBridgeFetcher bridge) {
|
||||||
|
this.cfg = cfg;
|
||||||
|
this.bridge = bridge;
|
||||||
|
this.providers = providers.stream().filter(IThreePidProvider::isEnabled).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
private void build() throws UnknownHostException {
|
private void build() throws UnknownHostException {
|
||||||
try {
|
try {
|
||||||
log.info("Found {} providers", providers.size());
|
log.info("Found {} providers", providers.size());
|
||||||
|
providers.forEach(p -> log.info("\t- {}", p.getClass().getName()));
|
||||||
providers.sort((o1, o2) -> Integer.compare(o2.getPriority(), o1.getPriority()));
|
providers.sort((o1, o2) -> Integer.compare(o2.getPriority(), o1.getPriority()));
|
||||||
|
|
||||||
log.info("Recursive lookup enabled: {}", recursiveCfg.isEnabled());
|
log.info("Recursive lookup enabled: {}", cfg.isEnabled());
|
||||||
for (String cidr : recursiveCfg.getAllowedCidr()) {
|
for (String cidr : cfg.getAllowedCidr()) {
|
||||||
log.info("{} is allowed for recursion", cidr);
|
log.info("{} is allowed for recursion", cidr);
|
||||||
allowedCidr.add(new CIDRUtils(cidr));
|
allowedCidr.add(new CIDRUtils(cidr));
|
||||||
}
|
}
|
||||||
@@ -75,7 +77,7 @@ public class RecursivePriorityLookupStrategy implements LookupStrategy {
|
|||||||
boolean canRecurse = false;
|
boolean canRecurse = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (recursiveCfg.isEnabled()) {
|
if (cfg.isEnabled()) {
|
||||||
log.debug("Checking {} CIDRs for recursion", allowedCidr.size());
|
log.debug("Checking {} CIDRs for recursion", allowedCidr.size());
|
||||||
for (CIDRUtils cidr : allowedCidr) {
|
for (CIDRUtils cidr : allowedCidr) {
|
||||||
if (cidr.isInRange(source)) {
|
if (cidr.isInRange(source)) {
|
||||||
@@ -106,7 +108,7 @@ public class RecursivePriorityLookupStrategy implements LookupStrategy {
|
|||||||
|
|
||||||
log.info("Host {} allowed for recursion: {}", request.getRequester(), canRecurse);
|
log.info("Host {} allowed for recursion: {}", request.getRequester(), canRecurse);
|
||||||
for (IThreePidProvider provider : providers) {
|
for (IThreePidProvider provider : providers) {
|
||||||
if (provider.isEnabled() && (provider.isLocal() || canRecurse || forceRecursive)) {
|
if (provider.isLocal() || canRecurse || forceRecursive) {
|
||||||
usableProviders.add(provider);
|
usableProviders.add(provider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,9 +161,9 @@ public class RecursivePriorityLookupStrategy implements LookupStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
recursiveCfg.getBridge() != null &&
|
cfg.getBridge() != null &&
|
||||||
recursiveCfg.getBridge().getEnabled() &&
|
cfg.getBridge().getEnabled() &&
|
||||||
(!recursiveCfg.getBridge().getRecursiveOnly() || isAllowedForRecursive(request.getRequester()))
|
(!cfg.getBridge().getRecursiveOnly() || isAllowedForRecursive(request.getRequester()))
|
||||||
) {
|
) {
|
||||||
log.info("Using bridge failover for lookup");
|
log.info("Using bridge failover for lookup");
|
||||||
return bridge.find(request);
|
return bridge.find(request);
|
||||||
|
@@ -66,9 +66,20 @@ sql:
|
|||||||
connection: ''
|
connection: ''
|
||||||
auth:
|
auth:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
directory:
|
||||||
|
enabled: false
|
||||||
|
query:
|
||||||
|
name:
|
||||||
|
value: 'SELECT 1'
|
||||||
|
threepid:
|
||||||
|
value: 'SELECT 1'
|
||||||
identity:
|
identity:
|
||||||
type: 'mxid'
|
type: 'mxid'
|
||||||
query: "SELECT user_id AS uid FROM user_threepids WHERE medium = ? AND address = ?"
|
query: 'SELECT user_id AS uid FROM user_threepids WHERE medium = ? AND address = ?'
|
||||||
|
|
||||||
|
synapseSql:
|
||||||
|
enabled: false
|
||||||
|
type: 'sqlite'
|
||||||
|
|
||||||
forward:
|
forward:
|
||||||
servers:
|
servers:
|
||||||
|
Reference in New Issue
Block a user