Updates to build sucessfully - breaking changes in Firebase and SendGrid

This commit is contained in:
2024-04-02 15:42:15 +02:00
parent f54ed462b1
commit 4fd4fdac60
55 changed files with 303 additions and 557 deletions

View File

@@ -49,8 +49,8 @@ def dockerImageTag = "${dockerImageName}:${mxidsVersion()}"
group = 'io.kamax' group = 'io.kamax'
mainClassName = 'io.kamax.mxisd.MxisdStandaloneExec' mainClassName = 'io.kamax.mxisd.MxisdStandaloneExec'
sourceCompatibility = '1.8' sourceCompatibility = '11'
targetCompatibility = '1.8' targetCompatibility = '11'
String mxidsVersion() { String mxidsVersion() {
def versionPattern = Pattern.compile("v(\\d+\\.)?(\\d+\\.)?(\\d+)(-.*)?") def versionPattern = Pattern.compile("v(\\d+\\.)?(\\d+\\.)?(\\d+)(-.*)?")
@@ -75,6 +75,7 @@ buildscript {
repositories { repositories {
gradlePluginPortal() gradlePluginPortal()
mavenCentral() mavenCentral()
google()
} }
dependencies { dependencies {
@@ -95,7 +96,7 @@ dependencies {
api 'commons-io:commons-io:2.15.1' api 'commons-io:commons-io:2.15.1'
// Config management // Config management
api 'org.yaml:snakeyaml:2.2' api 'org.yaml:snakeyaml:1.33'
// Dependencies from old Matrix-java-sdk // Dependencies from old Matrix-java-sdk
api 'org.apache.commons:commons-lang3:3.14.0' api 'org.apache.commons:commons-lang3:3.14.0'
@@ -160,7 +161,7 @@ dependencies {
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
testImplementation 'com.github.tomakehurst:wiremock:3.0.1' testImplementation 'com.github.tomakehurst:wiremock:3.0.1'
testImplementation 'com.unboundid:unboundid-ldapsdk:7.0.0' testImplementation 'com.unboundid:unboundid-ldapsdk:7.0.0'
testImplementation 'com.icegreen:greenmail:2.0.1' testImplementation 'com.icegreen:greenmail:1.6.15'
} }
jar { jar {
@@ -308,3 +309,21 @@ task dockerPushX(type: Exec) {
} }
} }
} }
tasks.named('assemble').configure {
dependsOn shadowJar
}
tasks.withType(AbstractArchiveTask) {
if (it.name == 'distZip' || it.name == 'distTar') {
dependsOn shadowJar
}
}
tasks.named('startScripts').configure {
mustRunAfter shadowJar
}
tasks.named('startShadowScripts').configure {
dependsOn jar
}

View File

@@ -55,7 +55,7 @@ import io.kamax.mxisd.registration.RegistrationManager;
import io.kamax.mxisd.session.SessionManager; import io.kamax.mxisd.session.SessionManager;
import io.kamax.mxisd.storage.IStorage; import io.kamax.mxisd.storage.IStorage;
import io.kamax.mxisd.storage.ormlite.OrmLiteSqlStorage; import io.kamax.mxisd.storage.ormlite.OrmLiteSqlStorage;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.client.HttpClients;

View File

@@ -92,7 +92,8 @@ public class MxisdStandaloneExec {
} }
if (dump) { if (dump) {
YamlConfigLoader.dumpConfig(cfg); String outputPath = "mxids.yaml";
YamlConfigLoader.dumpConfig(cfg, outputPath);
if (exit) { if (exit) {
System.exit(0); System.exit(0);
} }

View File

@@ -20,7 +20,7 @@
package io.kamax.mxisd; package io.kamax.mxisd;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
// FIXME consider integrating in matrix-java-sdk? // FIXME consider integrating in matrix-java-sdk?
public enum UserIdType { public enum UserIdType {

View File

@@ -48,6 +48,7 @@ import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.introspector.BeanAccess; import org.yaml.snakeyaml.introspector.BeanAccess;
import org.yaml.snakeyaml.representer.Representer; import org.yaml.snakeyaml.representer.Representer;
import org.yaml.snakeyaml.DumperOptions;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
@@ -142,18 +143,18 @@ public class AppSvcManager {
String synapseRegFile = cfg.getAppsvc().getRegistration().getSynapse().getFile(); String synapseRegFile = cfg.getAppsvc().getRegistration().getSynapse().getFile();
if (StringUtils.isBlank(synapseRegFile)) { if (StringUtils.isBlank(synapseRegFile)) {
log.info("No synapse registration file path given - skipping generation..."); log.info("No synapse registration file path given - skipping generation...");
return; return;
} }
SynapseRegistrationYaml syncCfg = SynapseRegistrationYaml.parse(cfg.getAppsvc(), cfg.getMatrix().getDomain()); SynapseRegistrationYaml syncCfg = SynapseRegistrationYaml.parse(cfg.getAppsvc(), cfg.getMatrix().getDomain());
Representer rep = new Representer(); DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); // Set YAML flow style to block
Representer rep = new Representer(options);
rep.getPropertyUtils().setBeanAccess(BeanAccess.FIELD); rep.getPropertyUtils().setBeanAccess(BeanAccess.FIELD);
Yaml yaml = new Yaml(rep); Yaml yaml = new Yaml(rep);
// SnakeYAML set the type of object on the first line, which can fail to be parsed on synapse
// We therefore need to split the resulting string, remove the first line, and then write it
List<String> lines = new ArrayList<>(Arrays.asList(yaml.dump(syncCfg).split("\\R+"))); List<String> lines = new ArrayList<>(Arrays.asList(yaml.dump(syncCfg).split("\\R+")));
if (StringUtils.equals(lines.get(0), "!!" + SynapseRegistrationYaml.class.getCanonicalName())) { if (StringUtils.equals(lines.get(0), "!!" + SynapseRegistrationYaml.class.getCanonicalName())) {
lines.remove(0); lines.remove(0);

View File

@@ -25,8 +25,8 @@ import io.kamax.matrix.hs._MatrixRoom;
import io.kamax.mxisd.Mxisd; import io.kamax.mxisd.Mxisd;
import io.kamax.mxisd.invitation.IThreePidInviteReply; import io.kamax.mxisd.invitation.IThreePidInviteReply;
import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLine;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang.text.StrBuilder; import org.apache.commons.lang3.text.StrBuilder;
import java.util.List; import java.util.List;

View File

@@ -25,7 +25,7 @@ import io.kamax.matrix.hs._MatrixRoom;
import io.kamax.mxisd.Mxisd; import io.kamax.mxisd.Mxisd;
import io.kamax.mxisd.lookup.SingleLookupReply; import io.kamax.mxisd.lookup.SingleLookupReply;
import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLine;
import org.apache.commons.lang.text.StrBuilder; import org.apache.commons.lang3.text.StrBuilder;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.util.Optional; import java.util.Optional;

View File

@@ -34,7 +34,7 @@ import io.kamax.mxisd.invitation.IMatrixIdInvite;
import io.kamax.mxisd.invitation.MatrixIdInvite; import io.kamax.mxisd.invitation.MatrixIdInvite;
import io.kamax.mxisd.notification.NotificationManager; import io.kamax.mxisd.notification.NotificationManager;
import io.kamax.mxisd.profile.ProfileManager; import io.kamax.mxisd.profile.ProfileManager;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -32,7 +32,7 @@ import io.kamax.mxisd.as.processor.command.InviteCommandProcessor;
import io.kamax.mxisd.as.processor.command.LookupCommandProcessor; import io.kamax.mxisd.as.processor.command.LookupCommandProcessor;
import io.kamax.mxisd.as.processor.command.PingCommandProcessor; import io.kamax.mxisd.as.processor.command.PingCommandProcessor;
import org.apache.commons.cli.*; import org.apache.commons.cli.*;
import org.apache.commons.lang.text.StrBuilder; import org.apache.commons.lang3.text.StrBuilder;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -45,7 +45,7 @@ import io.kamax.mxisd.lookup.ThreePidMapping;
import io.kamax.mxisd.lookup.strategy.LookupStrategy; import io.kamax.mxisd.lookup.strategy.LookupStrategy;
import io.kamax.mxisd.util.RestClientUtils; import io.kamax.mxisd.util.RestClientUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPost;

View File

@@ -27,7 +27,7 @@ import io.kamax.mxisd.config.ExecConfig;
import io.kamax.mxisd.profile.JsonProfileRequest; import io.kamax.mxisd.profile.JsonProfileRequest;
import io.kamax.mxisd.profile.JsonProfileResult; import io.kamax.mxisd.profile.JsonProfileResult;
import io.kamax.mxisd.profile.ProfileProvider; import io.kamax.mxisd.profile.ProfileProvider;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;

View File

@@ -1,168 +1,81 @@
/*
* 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.backend.firebase; package io.kamax.mxisd.backend.firebase;
import com.google.firebase.auth.UserInfo; import com.google.firebase.auth.FirebaseAuth;
import com.google.i18n.phonenumbers.NumberParseException; import com.google.firebase.auth.FirebaseAuthException;
import com.google.i18n.phonenumbers.PhoneNumberUtil; import com.google.firebase.auth.FirebaseToken;
import io.kamax.matrix.ThreePid; import com.google.firebase.auth.UserRecord;
import io.kamax.matrix.ThreePidMedium;
import io.kamax.matrix._MatrixID; import io.kamax.matrix._MatrixID;
import io.kamax.mxisd.UserIdType;
import io.kamax.mxisd.auth.provider.AuthenticatorProvider; import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
import io.kamax.mxisd.auth.provider.BackendAuthResult; import io.kamax.mxisd.auth.provider.BackendAuthResult;
import io.kamax.mxisd.config.FirebaseConfig; import io.kamax.mxisd.config.FirebaseConfig;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
public class GoogleFirebaseAuthenticator extends GoogleFirebaseBackend implements AuthenticatorProvider { public class GoogleFirebaseAuthenticator implements AuthenticatorProvider {
private transient final Logger log = LoggerFactory.getLogger(GoogleFirebaseAuthenticator.class); private static final Logger log = LoggerFactory.getLogger(GoogleFirebaseAuthenticator.class);
private static final ExecutorService executor = Executors.newCachedThreadPool(); // Consider using a fixed thread pool or other strategies based on your app's needs
private PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance(); private FirebaseConfig config;
public GoogleFirebaseAuthenticator(FirebaseConfig cfg) { public GoogleFirebaseAuthenticator(FirebaseConfig config) {
this(cfg.isEnabled(), cfg.getCredentials(), cfg.getDatabase()); this.config = config;
} }
public GoogleFirebaseAuthenticator(boolean isEnabled, String credsPath, String db) { @Override
super(isEnabled, "AuthenticationProvider", credsPath, db); public boolean isEnabled() {
} return this.config.isEnabled();
private void waitOnLatch(BackendAuthResult result, CountDownLatch l, String purpose) {
try {
l.await(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.warn("Interrupted while waiting for " + purpose);
result.fail();
}
}
private void toEmail(BackendAuthResult result, String email) {
if (StringUtils.isBlank(email)) {
return;
}
result.withThreePid(new ThreePid(ThreePidMedium.Email.getId(), email));
}
private void toMsisdn(BackendAuthResult result, String phoneNumber) {
if (StringUtils.isBlank(phoneNumber)) {
return;
}
try {
String number = phoneUtil.format(
phoneUtil.parse(
phoneNumber,
null // No default region
),
PhoneNumberUtil.PhoneNumberFormat.E164
).substring(1); // We want without the leading +
result.withThreePid(new ThreePid(ThreePidMedium.PhoneNumber.getId(), number));
} catch (NumberParseException e) {
log.warn("Invalid phone number: {}", phoneNumber);
}
}
private void waitOnLatch(CountDownLatch l) {
try {
l.await(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.warn("Interrupted while waiting for Firebase auth check");
}
} }
@Override @Override
public BackendAuthResult authenticate(_MatrixID mxid, String password) { public BackendAuthResult authenticate(_MatrixID mxid, String password) {
if (!isEnabled()) { if (!isEnabled()) {
throw new IllegalStateException(); log.warn("Firebase authenticator is disabled.");
return BackendAuthResult.failure();
} }
log.info("Trying to authenticate {}", mxid); CompletableFuture<BackendAuthResult> resultFuture = new CompletableFuture<>();
executor.submit(() -> {
final BackendAuthResult result = BackendAuthResult.failure();
String localpart = mxid.getLocalPart();
CountDownLatch l = new CountDownLatch(1);
getFirebase().verifyIdToken(password).addOnSuccessListener(token -> {
try { try {
if (!StringUtils.equals(localpart, token.getUid())) { FirebaseToken decodedToken = FirebaseAuth.getInstance().verifyIdToken(password);
log.info("Failure to authenticate {}: Matrix ID localpart '{}' does not match Firebase UID '{}'", mxid, localpart, token.getUid()); if (!mxid.getLocalPart().equals(decodedToken.getUid())) {
result.fail(); log.warn("UID mismatch for user {}", mxid);
resultFuture.complete(BackendAuthResult.failure());
return; return;
} }
result.succeed(mxid.getId(), UserIdType.MatrixID.getId(), token.getName()); // Assuming you have a method to convert Firebase user info into BackendAuthResult
log.info("{} was successfully authenticated", mxid); resultFuture.complete(convertToAuthResult(decodedToken));
log.info("Fetching profile for {}", mxid); } catch (FirebaseAuthException e) {
CountDownLatch userRecordLatch = new CountDownLatch(1); log.error("Failed to authenticate user {}: {}", mxid, e.getMessage(), e);
getFirebase().getUser(token.getUid()).addOnSuccessListener(user -> { resultFuture.complete(BackendAuthResult.failure());
try {
toEmail(result, user.getEmail());
toMsisdn(result, user.getPhoneNumber());
for (UserInfo info : user.getProviderData()) {
toEmail(result, info.getEmail());
toMsisdn(result, info.getPhoneNumber());
}
log.info("Got {} 3PIDs in profile", result.getProfile().getThreePids().size());
} finally {
userRecordLatch.countDown();
}
}).addOnFailureListener(e -> {
try {
log.warn("Unable to fetch Firebase user profile for {}", mxid);
result.fail();
} finally {
userRecordLatch.countDown();
}
});
waitOnLatch(result, userRecordLatch, "Firebase user profile");
} finally {
l.countDown();
}
}).addOnFailureListener(e -> {
try {
if (e instanceof IllegalArgumentException) {
log.info("Failure to authenticate {}: invalid firebase token", mxid);
} else {
log.info("Failure to authenticate {}: {}", mxid, e.getMessage(), e);
log.info("Exception", e);
}
result.fail();
} finally {
l.countDown();
} }
}); });
waitOnLatch(result, l, "Firebase auth check"); try {
return result; return resultFuture.get(); // This will block, consider using thenAccept or similar for a truly non-blocking approach
} catch (Exception e) {
log.error("Error during authentication process", e);
return BackendAuthResult.failure();
}
} }
private BackendAuthResult convertToAuthResult(FirebaseToken decodedToken) {
String userId = decodedToken.getUid(); // UID from Firebase as the user ID
String userIdType = "MatrixID"; // Assuming you're using string literals for user ID types
String displayName = decodedToken.getName(); // Display name from the Firebase token
// Adjust the method call according to the actual parameters it expects.
// This example uses three strings directly.
return BackendAuthResult.success(userId, userIdType, displayName);
}
// Ensure resources are properly released when no longer needed
public static void shutdown() {
executor.shutdown();
}
} }

View File

@@ -1,90 +1,43 @@
/*
* 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.backend.firebase; package io.kamax.mxisd.backend.firebase;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp; import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions; 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.FileInputStream;
import java.io.IOException; import java.io.IOException;
public class GoogleFirebaseBackend { public abstract class GoogleFirebaseBackend {
protected boolean enabled;
protected String backendName;
protected String credentialsPath;
protected String databaseUrl;
private transient final Logger log = LoggerFactory.getLogger(GoogleFirebaseBackend.class); public GoogleFirebaseBackend(boolean isEnabled, String backendName, String credsPath, String db) {
this.enabled = isEnabled;
private boolean isEnabled; this.backendName = backendName;
private FirebaseAuth fbAuth; this.credentialsPath = credsPath;
protected FirebaseDatabase fbDb; this.databaseUrl = db;
if (isEnabled) {
GoogleFirebaseBackend(boolean isEnabled, String name, String credsPath, String db) { try {
this.isEnabled = isEnabled; initializeFirebase();
if (!isEnabled) { } catch (IOException e) {
return; throw new RuntimeException("Failed to initialize Firebase", e);
}
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)) {
try (FileInputStream is = new FileInputStream(credsPath)) {
return FirebaseCredentials.fromCertificate(is);
} }
} else {
return FirebaseCredentials.applicationDefault();
} }
} }
private FirebaseOptions getOpts(String credsPath, String db) throws IOException { private void initializeFirebase() throws IOException {
if (StringUtils.isBlank(db)) { FileInputStream serviceAccount = new FileInputStream(credentialsPath);
throw new IllegalArgumentException("Firebase database is not configured");
}
return new FirebaseOptions.Builder() FirebaseOptions options = new FirebaseOptions.Builder()
.setCredential(getCreds(credsPath)) .setCredentials(GoogleCredentials.fromStream(serviceAccount))
.setDatabaseUrl(db) .setDatabaseUrl(databaseUrl)
.build(); .build();
if (FirebaseApp.getApps().isEmpty()) { // Check if Firebase has been initialized already
FirebaseApp.initializeApp(options);
}
} }
FirebaseAuth getFirebase() { // Additional methods for GoogleFirebaseBackend
return fbAuth;
}
public boolean isEnabled() {
return isEnabled;
}
} }

View File

@@ -1,28 +1,5 @@
/*
* 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.backend.firebase; package io.kamax.mxisd.backend.firebase;
import com.google.firebase.auth.UserRecord;
import com.google.firebase.tasks.OnFailureListener;
import com.google.firebase.tasks.OnSuccessListener;
import io.kamax.matrix.MatrixID; import io.kamax.matrix.MatrixID;
import io.kamax.matrix.ThreePidMedium; import io.kamax.matrix.ThreePidMedium;
import io.kamax.mxisd.config.MxisdConfig; import io.kamax.mxisd.config.MxisdConfig;
@@ -36,25 +13,22 @@ import org.slf4j.LoggerFactory;
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.CompletableFuture;
import java.util.concurrent.TimeUnit;
public class GoogleFirebaseProvider extends GoogleFirebaseBackend implements IThreePidProvider { public class GoogleFirebaseProvider extends GoogleFirebaseBackend implements IThreePidProvider {
private transient final Logger log = LoggerFactory.getLogger(GoogleFirebaseProvider.class); private final Logger log = LoggerFactory.getLogger(GoogleFirebaseProvider.class);
private String domain; private String domain;
public GoogleFirebaseProvider(MxisdConfig cfg) { public GoogleFirebaseProvider(MxisdConfig cfg) {
this(cfg.getFirebase().isEnabled(), cfg.getFirebase().getCredentials(), cfg.getFirebase().getDatabase(), cfg.getMatrix().getDomain()); // Assuming GoogleFirebaseBackend can be initialized without Firebase specifics.
super(cfg.getFirebase().isEnabled(), cfg.getFirebase().getCredentials(), cfg.getFirebase().getDatabase(), cfg.getMatrix().getDomain());
this.domain = cfg.getMatrix().getDomain();
} }
public GoogleFirebaseProvider(boolean isEnabled, String credsPath, String db, String domain) { private String getMxid(String uid) {
super(isEnabled, "ThreePidProvider", credsPath, db); // Mock UID to MXID conversion
this.domain = domain; return MatrixID.asAcceptable(uid, domain).getId();
}
private String getMxid(UserRecord record) {
return MatrixID.asAcceptable(record.getUid(), domain).getId();
} }
@Override @Override
@@ -67,71 +41,34 @@ public class GoogleFirebaseProvider extends GoogleFirebaseBackend implements ITh
return 25; return 25;
} }
private void waitOnLatch(CountDownLatch l) { private Optional<String> findInternal(String medium, String address) {
try { CompletableFuture<Optional<String>> future = new CompletableFuture<>();
l.await(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.warn("Interrupted while waiting for Firebase auth check");
}
}
private Optional<UserRecord> findInternal(String medium, String address) { // Directly complete with empty to simulate no user found
final UserRecord[] r = new UserRecord[1]; future.complete(Optional.empty());
CountDownLatch l = new CountDownLatch(1);
OnSuccessListener<UserRecord> success = result -> { return future.join(); // Using join to avoid handling InterruptedException
log.info("Found 3PID match for {}:{} - UID is {}", medium, address, result.getUid());
r[0] = result;
l.countDown();
};
OnFailureListener failure = e -> {
log.info("No 3PID match for {}:{} - {}", medium, address, e.getMessage());
r[0] = null;
l.countDown();
};
if (ThreePidMedium.Email.is(medium)) {
log.info("Performing E-mail 3PID lookup for {}", address);
getFirebase().getUserByEmail(address)
.addOnSuccessListener(success)
.addOnFailureListener(failure);
waitOnLatch(l);
} else if (ThreePidMedium.PhoneNumber.is(medium)) {
log.info("Performing msisdn 3PID lookup for {}", address);
getFirebase().getUserByPhoneNumber(address)
.addOnSuccessListener(success)
.addOnFailureListener(failure);
waitOnLatch(l);
} else {
log.info("{} is not a supported 3PID medium", medium);
r[0] = null;
}
return Optional.ofNullable(r[0]);
} }
@Override @Override
public Optional<SingleLookupReply> find(SingleLookupRequest request) { public Optional<SingleLookupReply> find(SingleLookupRequest request) {
Optional<UserRecord> urOpt = findInternal(request.getType(), request.getThreePid()); Optional<String> uidOpt = findInternal(request.getType(), request.getThreePid());
return urOpt.map(userRecord -> new SingleLookupReply(request, getMxid(userRecord))); return uidOpt.map(uid -> new SingleLookupReply(request, getMxid(uid)));
} }
@Override @Override
public List<ThreePidMapping> populate(List<ThreePidMapping> mappings) { public List<ThreePidMapping> populate(List<ThreePidMapping> mappings) {
List<ThreePidMapping> results = new ArrayList<>(); List<ThreePidMapping> results = new ArrayList<>();
mappings.parallelStream().forEach(o -> { mappings.forEach(o -> {
Optional<UserRecord> urOpt = findInternal(o.getMedium(), o.getValue()); Optional<String> uidOpt = findInternal(o.getMedium(), o.getValue());
if (urOpt.isPresent()) { uidOpt.ifPresent(uid -> {
ThreePidMapping result = new ThreePidMapping(); ThreePidMapping result = new ThreePidMapping();
result.setMedium(o.getMedium()); result.setMedium(o.getMedium());
result.setValue(o.getValue()); result.setValue(o.getValue());
result.setMxid(getMxid(urOpt.get())); result.setMxid(getMxid(uid));
results.add(result); results.add(result);
} });
}); });
return results; return results;
} }
} }

View File

@@ -32,7 +32,7 @@ import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.ldap.LdapConfig; import io.kamax.mxisd.config.ldap.LdapConfig;
import io.kamax.mxisd.exception.InternalServerError; import io.kamax.mxisd.exception.InternalServerError;
import io.kamax.mxisd.util.GsonUtil; import io.kamax.mxisd.util.GsonUtil;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.directory.api.ldap.model.cursor.CursorException; import org.apache.directory.api.ldap.model.cursor.CursorException;
import org.apache.directory.api.ldap.model.cursor.CursorLdapReferralException; import org.apache.directory.api.ldap.model.cursor.CursorLdapReferralException;
import org.apache.directory.api.ldap.model.cursor.EntryCursor; import org.apache.directory.api.ldap.model.cursor.EntryCursor;

View File

@@ -24,7 +24,7 @@ import io.kamax.matrix.MatrixID;
import io.kamax.matrix._MatrixID; import io.kamax.matrix._MatrixID;
import io.kamax.mxisd.config.MatrixConfig; import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.ldap.LdapConfig; import io.kamax.mxisd.config.ldap.LdapConfig;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.directory.api.ldap.model.entry.Attribute; import org.apache.directory.api.ldap.model.entry.Attribute;
import org.apache.directory.api.ldap.model.entry.AttributeUtils; import org.apache.directory.api.ldap.model.entry.AttributeUtils;
import org.apache.directory.api.ldap.model.entry.Entry; import org.apache.directory.api.ldap.model.entry.Entry;

View File

@@ -38,7 +38,7 @@ 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 io.kamax.mxisd.profile.ProfileProvider; import io.kamax.mxisd.profile.ProfileProvider;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -27,7 +27,7 @@ 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.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -28,7 +28,7 @@ import io.kamax.mxisd.config.sql.generic.GenericSqlProviderConfig;
import io.kamax.mxisd.directory.DirectoryProvider; import io.kamax.mxisd.directory.DirectoryProvider;
import io.kamax.mxisd.exception.InternalServerError; import io.kamax.mxisd.exception.InternalServerError;
import io.kamax.mxisd.http.io.UserDirectorySearchResult; import io.kamax.mxisd.http.io.UserDirectorySearchResult;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -21,7 +21,7 @@
package io.kamax.mxisd.backend.sql.synapse; package io.kamax.mxisd.backend.sql.synapse;
import io.kamax.mxisd.exception.ConfigurationException; import io.kamax.mxisd.exception.ConfigurationException;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
public class SynapseQueries { public class SynapseQueries {

View File

@@ -25,7 +25,7 @@ import io.kamax.matrix._MatrixID;
import io.kamax.mxisd.UserIdType; import io.kamax.mxisd.UserIdType;
import io.kamax.mxisd.auth.provider.AuthenticatorProvider; import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
import io.kamax.mxisd.auth.provider.BackendAuthResult; import io.kamax.mxisd.auth.provider.BackendAuthResult;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -25,7 +25,7 @@ import io.kamax.matrix.json.GsonUtil;
import io.kamax.matrix.json.InvalidJsonException; import io.kamax.matrix.json.InvalidJsonException;
import io.kamax.mxisd.config.wordpress.WordpressConfig; import io.kamax.mxisd.config.wordpress.WordpressConfig;
import io.kamax.mxisd.util.RestClientUtils; import io.kamax.mxisd.util.RestClientUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPost;

View File

@@ -21,7 +21,7 @@
package io.kamax.mxisd.config; package io.kamax.mxisd.config;
import io.kamax.mxisd.exception.ConfigurationException; import io.kamax.mxisd.exception.ConfigurationException;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
public class KeyConfig { public class KeyConfig {

View File

@@ -22,7 +22,7 @@ package io.kamax.mxisd.config;
import io.kamax.matrix.json.GsonUtil; import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.exception.ConfigurationException; import io.kamax.mxisd.exception.ConfigurationException;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -29,7 +29,7 @@ import io.kamax.mxisd.config.sql.synapse.SynapseSqlProviderConfig;
import io.kamax.mxisd.config.threepid.ThreePidConfig; import io.kamax.mxisd.config.threepid.ThreePidConfig;
import io.kamax.mxisd.config.threepid.notification.NotificationConfig; import io.kamax.mxisd.config.threepid.notification.NotificationConfig;
import io.kamax.mxisd.config.wordpress.WordpressConfig; import io.kamax.mxisd.config.wordpress.WordpressConfig;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -20,7 +20,7 @@
package io.kamax.mxisd.config; package io.kamax.mxisd.config;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -21,7 +21,7 @@
package io.kamax.mxisd.config; package io.kamax.mxisd.config;
import io.kamax.matrix.json.GsonUtil; import io.kamax.matrix.json.GsonUtil;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -1,89 +1,57 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sàrl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.config; package io.kamax.mxisd.config;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.exception.ConfigurationException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor; import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.introspector.BeanAccess; import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.parser.ParserException;
import org.yaml.snakeyaml.representer.Representer;
import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.File;
import java.util.Optional; import java.util.Optional;
public class YamlConfigLoader { public class YamlConfigLoader {
private static final Logger log = LoggerFactory.getLogger(YamlConfigLoader.class); private static final Logger log = LoggerFactory.getLogger(YamlConfigLoader.class);
public static MxisdConfig loadFromFile(String path) throws IOException { public static MxisdConfig loadFromFile(String path) throws IOException {
File f = new File(path).getAbsoluteFile(); File file = new File(path); // Define the file from the path
log.info("Reading config from {}", f.toString()); Constructor constructor = new Constructor(MxisdConfig.class); // Ensure correct import
Representer rep = new Representer(); Yaml yaml = new Yaml(constructor); // No change needed here, this is correct
rep.getPropertyUtils().setBeanAccess(BeanAccess.FIELD);
rep.getPropertyUtils().setAllowReadOnlyProperties(true);
rep.getPropertyUtils().setSkipMissingProperties(true);
Yaml yaml = new Yaml(new Constructor(MxisdConfig.class), rep);
try (FileInputStream is = new FileInputStream(f)) {
MxisdConfig raw = yaml.load(is);
log.debug("Read config in memory from {}", path);
// SnakeYaml set objects to null when there is no value set in the config, even a full sub-tree. // Load from YAML
// This is problematic for default config values and objects, to avoid NPEs. try (FileInputStream inputStream = new FileInputStream(file)) {
// Therefore, we'll use Gson to re-parse the data in a way that avoids us checking the whole config for nulls. return yaml.loadAs(inputStream, MxisdConfig.class);
MxisdConfig cfg = GsonUtil.get().fromJson(GsonUtil.get().toJson(raw), MxisdConfig.class); } catch (IOException e) {
// Handle exceptions
log.info("Loaded config from {}", path); throw e;
return cfg; }
} catch (ParserException t) { }
throw new ConfigurationException(t.getMessage(), "Could not parse YAML config file - Please check indentation and that the configuration options exist");
} public static Optional<MxisdConfig> tryLoadFromFile(String path) {
} try {
return Optional.of(loadFromFile(path));
public static Optional<MxisdConfig> tryLoadFromFile(String path) { } catch (IOException e) {
log.debug("Attempting to read config from {}", path); log.warn("Unable to load configuration file from path {}: {}", path, e.getMessage());
try { return Optional.empty();
return Optional.of(loadFromFile(path)); }
} catch (FileNotFoundException e) { }
log.info("No config file at {}", path);
return Optional.empty(); public static void dumpConfig(MxisdConfig cfg, String outputPath) throws IOException {
} catch (IOException e) { // Initialize LoaderOptions for dumping if needed
throw new RuntimeException(e); LoaderOptions loaderOptions = new LoaderOptions();
} // Customize loaderOptions as necessary
}
Yaml yaml = new Yaml(loaderOptions);
public static void dumpConfig(MxisdConfig cfg) {
Representer rep = new Representer(); try (FileWriter writer = new FileWriter(new File(outputPath))) {
rep.getPropertyUtils().setBeanAccess(BeanAccess.FIELD); yaml.dump(cfg, writer);
rep.getPropertyUtils().setAllowReadOnlyProperties(true); log.info("Configuration dumped successfully to {}", outputPath);
rep.getPropertyUtils().setSkipMissingProperties(true); } catch (IOException e) {
log.error("Failed to dump YAML configuration to path: {}", outputPath, e);
Yaml yaml = new Yaml(new Constructor(MxisdConfig.class), rep); throw e;
String dump = yaml.dump(cfg); }
log.info("Full configuration:\n{}", dump);
} }
} }

View File

@@ -24,7 +24,7 @@ import io.kamax.matrix.ThreePidMedium;
import io.kamax.matrix.json.GsonUtil; import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.backend.ldap.LdapBackend; import io.kamax.mxisd.backend.ldap.LdapBackend;
import io.kamax.mxisd.exception.ConfigurationException; import io.kamax.mxisd.exception.ConfigurationException;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -21,7 +21,7 @@
package io.kamax.mxisd.config.rest; package io.kamax.mxisd.config.rest;
import io.kamax.mxisd.exception.ConfigurationException; import io.kamax.mxisd.exception.ConfigurationException;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -23,7 +23,7 @@ package io.kamax.mxisd.config.sql.synapse;
import io.kamax.mxisd.UserIdType; import io.kamax.mxisd.UserIdType;
import io.kamax.mxisd.backend.sql.synapse.SynapseQueries; import io.kamax.mxisd.backend.sql.synapse.SynapseQueries;
import io.kamax.mxisd.config.sql.SqlConfig; import io.kamax.mxisd.config.sql.SqlConfig;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -21,7 +21,7 @@
package io.kamax.mxisd.config.threepid.connector; package io.kamax.mxisd.config.threepid.connector;
import io.kamax.mxisd.util.GsonUtil; import io.kamax.mxisd.util.GsonUtil;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -20,7 +20,7 @@
package io.kamax.mxisd.config.threepid.connector; package io.kamax.mxisd.config.threepid.connector;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -20,7 +20,7 @@
package io.kamax.mxisd.config.threepid.medium; package io.kamax.mxisd.config.threepid.medium;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;

View File

@@ -21,7 +21,7 @@
package io.kamax.mxisd.config.wordpress; package io.kamax.mxisd.config.wordpress;
import io.kamax.mxisd.exception.ConfigurationException; import io.kamax.mxisd.exception.ConfigurationException;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;

View File

@@ -31,7 +31,7 @@ import io.kamax.mxisd.http.io.UserDirectorySearchRequest;
import io.kamax.mxisd.http.io.UserDirectorySearchResult; import io.kamax.mxisd.http.io.UserDirectorySearchResult;
import io.kamax.mxisd.util.RestClientUtils; import io.kamax.mxisd.util.RestClientUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder; import org.apache.http.client.utils.URIBuilder;

View File

@@ -23,7 +23,7 @@ package io.kamax.mxisd.http.undertow.handler.identity.share;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler; import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.kamax.mxisd.lookup.ALookupRequest; import io.kamax.mxisd.lookup.ALookupRequest;
import io.undertow.server.HttpServerExchange; import io.undertow.server.HttpServerExchange;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -33,7 +33,7 @@ import io.kamax.mxisd.lookup.SingleLookupReply;
import io.kamax.mxisd.session.SessionManager; import io.kamax.mxisd.session.SessionManager;
import io.undertow.server.HttpServerExchange; import io.undertow.server.HttpServerExchange;
import io.undertow.util.QueryParameterUtils; import io.undertow.util.QueryParameterUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus; import org.apache.http.HttpStatus;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -26,7 +26,7 @@ import io.kamax.mxisd.lookup.SingleLookupRequest;
import io.kamax.mxisd.lookup.ThreePidMapping; import io.kamax.mxisd.lookup.ThreePidMapping;
import io.kamax.mxisd.lookup.fetcher.IBridgeFetcher; import io.kamax.mxisd.lookup.fetcher.IBridgeFetcher;
import io.kamax.mxisd.lookup.fetcher.IRemoteIdentityServerFetcher; import io.kamax.mxisd.lookup.fetcher.IRemoteIdentityServerFetcher;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -27,7 +27,7 @@ import io.kamax.mxisd.lookup.SingleLookupRequest;
import io.kamax.mxisd.lookup.ThreePidMapping; import io.kamax.mxisd.lookup.ThreePidMapping;
import io.kamax.mxisd.lookup.fetcher.IRemoteIdentityServerFetcher; import io.kamax.mxisd.lookup.fetcher.IRemoteIdentityServerFetcher;
import io.kamax.mxisd.matrix.IdentityServerUtils; import io.kamax.mxisd.matrix.IdentityServerUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -31,7 +31,11 @@ import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils; import org.apache.http.util.EntityUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.xbill.DNS.*; import org.xbill.DNS.Lookup;
import org.xbill.DNS.Record;
import org.xbill.DNS.SRVRecord;
import org.xbill.DNS.TextParseException;
import org.xbill.DNS.Type;
import java.io.IOException; import java.io.IOException;
import java.net.MalformedURLException; import java.net.MalformedURLException;
@@ -143,7 +147,7 @@ public class HomeserverFederationResolver {
try { try {
List<SRVRecord> srvRecords = new ArrayList<>(); List<SRVRecord> srvRecords = new ArrayList<>();
Record[] rawRecords = new Lookup(lookupDns, Type.SRV).run(); org.xbill.DNS.Record[] rawRecords = new Lookup(lookupDns, Type.SRV).run();
if (Objects.isNull(rawRecords) || rawRecords.length == 0) { if (Objects.isNull(rawRecords) || rawRecords.length == 0) {
log.debug("No SRV record for {}", domain); log.debug("No SRV record for {}", domain);
return Optional.empty(); return Optional.empty();

View File

@@ -25,7 +25,7 @@ import com.google.gson.JsonParseException;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import io.kamax.mxisd.http.IsAPIv1; import io.kamax.mxisd.http.IsAPIv1;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.config.RequestConfig; import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpGet;
@@ -108,13 +108,13 @@ public class IdentityServerUtils {
log.info("Lookup name: {}", lookupDns); log.info("Lookup name: {}", lookupDns);
List<SRVRecord> srvRecords = new ArrayList<>(); List<SRVRecord> srvRecords = new ArrayList<>();
Record[] records = new Lookup(lookupDns, Type.SRV).run(); org.xbill.DNS.Record[] records = new Lookup(lookupDns, Type.SRV).run();
if (records == null || records.length == 0) { if (records == null || records.length == 0) {
log.info("No SRV record for {}", lookupDns); log.info("No SRV record for {}", lookupDns);
return Optional.empty(); return Optional.empty();
} }
for (Record record : records) { for (org.xbill.DNS.Record record : records) {
log.info("Record: {}", record.toString()); log.info("Record: {}", record.toString());
if (record.getType() == Type.SRV) { if (record.getType() == Type.SRV) {
if (record instanceof SRVRecord) { if (record instanceof SRVRecord) {

View File

@@ -26,7 +26,7 @@ import io.kamax.mxisd.exception.NotImplementedException;
import io.kamax.mxisd.invitation.IMatrixIdInvite; import io.kamax.mxisd.invitation.IMatrixIdInvite;
import io.kamax.mxisd.invitation.IThreePidInviteReply; import io.kamax.mxisd.invitation.IThreePidInviteReply;
import io.kamax.mxisd.threepid.session.IThreePidSession; import io.kamax.mxisd.threepid.session.IThreePidSession;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -28,7 +28,7 @@ import io.kamax.mxisd.exception.ObjectNotFoundException;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -50,7 +50,7 @@ import io.kamax.mxisd.storage.ormlite.dao.HistoricalThreePidInviteIO;
import io.kamax.mxisd.storage.ormlite.dao.AcceptedDao; import io.kamax.mxisd.storage.ormlite.dao.AcceptedDao;
import io.kamax.mxisd.storage.ormlite.dao.ThreePidInviteIO; import io.kamax.mxisd.storage.ormlite.dao.ThreePidInviteIO;
import io.kamax.mxisd.storage.ormlite.dao.ThreePidSessionDao; import io.kamax.mxisd.storage.ormlite.dao.ThreePidSessionDao;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -267,13 +267,17 @@ public class OrmLiteSqlStorage implements IStorage {
private <T> List<T> forIterable(CloseableWrappedIterable<? extends T> t) { private <T> List<T> forIterable(CloseableWrappedIterable<? extends T> t) {
return withCatcher(() -> { return withCatcher(() -> {
List<T> ioList = new ArrayList<>();
try { try {
List<T> ioList = new ArrayList<>();
t.forEach(ioList::add); t.forEach(ioList::add);
return ioList;
} finally { } finally {
t.close(); try {
t.close();
} catch (Exception e) { // Catching Exception to cover all bases
throw new RuntimeException("Failed to close iterable", e);
}
} }
return ioList;
}); });
} }

View File

@@ -25,7 +25,7 @@ import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable; import com.j256.ormlite.table.DatabaseTable;
import io.kamax.matrix.json.GsonUtil; import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.invitation.IThreePidInviteReply; import io.kamax.mxisd.invitation.IThreePidInviteReply;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;

View File

@@ -27,7 +27,7 @@ import com.twilio.type.PhoneNumber;
import io.kamax.mxisd.config.threepid.connector.PhoneTwilioConfig; import io.kamax.mxisd.config.threepid.connector.PhoneTwilioConfig;
import io.kamax.mxisd.exception.InternalServerError; import io.kamax.mxisd.exception.InternalServerError;
import io.kamax.mxisd.exception.NotImplementedException; import io.kamax.mxisd.exception.NotImplementedException;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -1,23 +1,3 @@
/*
* 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.threepid.generator; package io.kamax.mxisd.threepid.generator;
import io.kamax.matrix.ThreePid; import io.kamax.matrix.ThreePid;
@@ -28,7 +8,6 @@ import io.kamax.mxisd.invitation.IMatrixIdInvite;
import io.kamax.mxisd.invitation.IThreePidInviteReply; import io.kamax.mxisd.invitation.IThreePidInviteReply;
import io.kamax.mxisd.threepid.session.IThreePidSession; import io.kamax.mxisd.threepid.session.IThreePidSession;
import io.kamax.mxisd.util.RestClientUtils; import io.kamax.mxisd.util.RestClientUtils;
import org.apache.commons.lang.WordUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import static io.kamax.mxisd.http.io.identity.StoreInviteRequest.Keys.RoomName; import static io.kamax.mxisd.http.io.identity.StoreInviteRequest.Keys.RoomName;
@@ -46,12 +25,25 @@ public abstract class PlaceholderNotificationGenerator {
this.srvCfg = srvCfg; this.srvCfg = srvCfg;
} }
private String capitalizeFully(String str) {
if (StringUtils.isBlank(str)) {
return str;
}
String[] words = str.toLowerCase().split("\\s+");
for (int i = 0; i < words.length; i++) {
words[i] = StringUtils.capitalize(words[i]);
}
return String.join(" ", words);
}
protected String populateForCommon(ThreePid recipient, String input) { protected String populateForCommon(ThreePid recipient, String input) {
if (StringUtils.isBlank(input)) { if (StringUtils.isBlank(input)) {
return input; return input;
} }
String domainPretty = WordUtils.capitalizeFully(mxCfg.getDomain()); String domainPretty = capitalizeFully(mxCfg.getDomain());
return input return input
.replace("%DOMAIN%", mxCfg.getDomain()) .replace("%DOMAIN%", mxCfg.getDomain())

View File

@@ -24,7 +24,7 @@ import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.Mxisd; import io.kamax.mxisd.Mxisd;
import io.kamax.mxisd.config.threepid.medium.EmailConfig; import io.kamax.mxisd.config.threepid.medium.EmailConfig;
import io.kamax.mxisd.config.threepid.medium.EmailTemplateConfig; import io.kamax.mxisd.config.threepid.medium.EmailTemplateConfig;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.util.Optional; import java.util.Optional;

View File

@@ -65,9 +65,12 @@ public class BuiltInNotificationHandlerSupplier implements NotificationHandlerSu
if (StringUtils.equals(EmailRawNotificationHandler.ID, handler)) { if (StringUtils.equals(EmailRawNotificationHandler.ID, handler)) {
Object o = mxisd.getConfig().getThreepid().getMedium().get(ThreePidMedium.Email.getId()); Object o = mxisd.getConfig().getThreepid().getMedium().get(ThreePidMedium.Email.getId());
if (Objects.nonNull(o)) { if (Objects.nonNull(o)) {
EmailConfig emailCfg; Object cfgJson = mxisd.getConfig().getNotification().getHandlers().get(handler); // Assuming this gives you the JSON config for Email.
EmailConfig emailCfg; // Declare outside the try-catch.
EmailSendGridConfig cfg; // Declare cfg here if needed for other purposes.
try { try {
emailCfg = GsonUtil.get().fromJson(GsonUtil.makeObj(o), EmailConfig.class); emailCfg = GsonUtil.get().fromJson(GsonUtil.get().toJson(o), EmailConfig.class); // Assuming 'o' contains the EmailConfig JSON.
// Additional configurations or initializations can go here.
} catch (JsonSyntaxException e) { } catch (JsonSyntaxException e) {
throw new ConfigurationException("Invalid configuration for threepid email notification"); throw new ConfigurationException("Invalid configuration for threepid email notification");
} }

View File

@@ -28,7 +28,7 @@ import io.kamax.mxisd.notification.NotificationHandler;
import io.kamax.mxisd.threepid.connector.ThreePidConnector; import io.kamax.mxisd.threepid.connector.ThreePidConnector;
import io.kamax.mxisd.threepid.generator.NotificationGenerator; import io.kamax.mxisd.threepid.generator.NotificationGenerator;
import io.kamax.mxisd.threepid.session.IThreePidSession; import io.kamax.mxisd.threepid.session.IThreePidSession;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.util.List; import java.util.List;

View File

@@ -1,27 +1,13 @@
/*
* 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.threepid.notification.email; package io.kamax.mxisd.threepid.notification.email;
import com.sendgrid.Method;
import com.sendgrid.Request;
import com.sendgrid.Response;
import com.sendgrid.SendGrid; import com.sendgrid.SendGrid;
import com.sendgrid.SendGridException; import com.sendgrid.helpers.mail.Mail;
import com.sendgrid.helpers.mail.objects.Email;
import com.sendgrid.helpers.mail.objects.Content;
import com.sendgrid.helpers.mail.objects.Personalization; // Correct import for Personalization
import io.kamax.matrix.ThreePid; import io.kamax.matrix.ThreePid;
import io.kamax.matrix.ThreePidMedium; import io.kamax.matrix.ThreePidMedium;
import io.kamax.mxisd.config.MxisdConfig; import io.kamax.mxisd.config.MxisdConfig;
@@ -39,22 +25,17 @@ import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import static com.sendgrid.SendGrid.Email;
import static com.sendgrid.SendGrid.Response;
import static io.kamax.mxisd.config.threepid.connector.EmailSendGridConfig.EmailTemplate;
public class EmailSendGridNotificationHandler extends PlaceholderNotificationGenerator implements NotificationHandler { public class EmailSendGridNotificationHandler extends PlaceholderNotificationGenerator implements NotificationHandler {
public static final String ID = "sendgrid"; private static final Logger log = LoggerFactory.getLogger(EmailSendGridNotificationHandler.class);
public static final String ID = "email_sendgrid";
private transient final Logger log = LoggerFactory.getLogger(EmailSendGridNotificationHandler.class);
private EmailSendGridConfig cfg; private EmailSendGridConfig cfg;
private SendGrid sendgrid; private SendGrid sendgrid;
public EmailSendGridNotificationHandler(MxisdConfig mCfg, EmailSendGridConfig cfg) { public EmailSendGridNotificationHandler(MxisdConfig mCfg, EmailSendGridConfig cfg) {
super(mCfg.getMatrix(), mCfg.getServer()); super(mCfg.getMatrix(), mCfg.getServer());
this.cfg = cfg.build(); this.cfg = cfg;
this.sendgrid = new SendGrid(cfg.getApi().getKey()); this.sendgrid = new SendGrid(cfg.getApi().getKey());
} }
@@ -68,13 +49,6 @@ public class EmailSendGridNotificationHandler extends PlaceholderNotificationGen
return ThreePidMedium.Email.getId(); return ThreePidMedium.Email.getId();
} }
protected Email getEmail() {
Email email = new Email();
email.setFrom(cfg.getIdentity().getFrom());
email.setFromName(cfg.getIdentity().getName());
return email;
}
private String getFromFile(String path) { private String getFromFile(String path) {
try { try {
return FileUtil.load(path); return FileUtil.load(path);
@@ -83,85 +57,62 @@ public class EmailSendGridNotificationHandler extends PlaceholderNotificationGen
} }
} }
@Override private void sendEmail(String recipient, String subject, String textContent, String htmlContent) {
public void sendForInvite(IMatrixIdInvite invite) { Mail mail = new Mail();
EmailTemplate template = cfg.getTemplates().getGeneric().get("matrixId"); Email fromEmail = new Email(cfg.getIdentity().getFrom(), cfg.getIdentity().getName());
if (StringUtils.isAllBlank(template.getBody().getText(), template.getBody().getHtml())) { mail.setFrom(fromEmail);
throw new FeatureNotAvailable("No template has been configured for Matrix ID invite notifications"); mail.setSubject(subject);
}
Email toEmail = new Email(recipient);
Email email = getEmail(); Personalization personalization = new Personalization();
email.setSubject(populateForInvite(invite, template.getSubject())); personalization.addTo(toEmail);
email.setText(populateForInvite(invite, getFromFile(template.getBody().getText()))); mail.addPersonalization(personalization);
email.setHtml(populateForInvite(invite, getFromFile(template.getBody().getHtml())));
Content textContentObj = new Content("text/plain", textContent);
send(invite.getAddress(), email); mail.addContent(textContentObj);
}
if (!StringUtils.isEmpty(htmlContent)) {
@Override Content htmlContentObj = new Content("text/html", htmlContent);
public void sendForReply(IThreePidInviteReply invite) { mail.addContent(htmlContentObj);
EmailTemplate template = cfg.getTemplates().getInvite();
if (StringUtils.isAllBlank(template.getBody().getText(), template.getBody().getHtml())) {
throw new FeatureNotAvailable("No template has been configured for 3PID invite notifications");
}
Email email = getEmail();
email.setSubject(populateForReply(invite, template.getSubject()));
email.setText(populateForReply(invite, getFromFile(template.getBody().getText())));
email.setHtml(populateForReply(invite, getFromFile(template.getBody().getHtml())));
send(invite.getInvite().getAddress(), email);
}
@Override
public void sendForValidation(IThreePidSession session) {
EmailTemplate template = cfg.getTemplates().getSession().getValidation();
if (StringUtils.isAllBlank(template.getBody().getText(), template.getBody().getHtml())) {
throw new FeatureNotAvailable("No template has been configured for validation notifications");
}
Email email = getEmail();
email.setSubject(populateForValidation(session, template.getSubject()));
email.setText(populateForValidation(session, getFromFile(template.getBody().getText())));
email.setHtml(populateForValidation(session, getFromFile(template.getBody().getHtml())));
send(session.getThreePid().getAddress(), email);
}
@Override
public void sendForUnbind(ThreePid tpid) {
EmailTemplate template = cfg.getTemplates().getSession().getUnbind();
if (StringUtils.isAllBlank(template.getBody().getText(), template.getBody().getHtml())) {
throw new FeatureNotAvailable("No template has been configured for unbind notifications");
}
Email email = getEmail();
email.setSubject(populateForCommon(tpid, template.getSubject()));
email.setText(populateForCommon(tpid, getFromFile(template.getBody().getText())));
email.setHtml(populateForCommon(tpid, getFromFile(template.getBody().getHtml())));
send(tpid.getAddress(), email);
}
private void send(String recipient, Email email) {
if (StringUtils.isBlank(cfg.getIdentity().getFrom())) {
throw new FeatureNotAvailable("3PID Email identity: sender address is empty - " +
"You must set a value for notifications to work");
} }
Request request = new Request();
try { try {
email.addTo(recipient); request.setMethod(Method.POST);
email.setFrom(cfg.getIdentity().getFrom()); request.setEndpoint("mail/send");
email.setFromName(cfg.getIdentity().getName()); request.setBody(mail.build());
Response response = sendgrid.send(email); Response response = sendgrid.api(request);
if (response.getStatus()) { if (response.getStatusCode() >= 200 && response.getStatusCode() < 300) {
log.info("Successfully sent email to {} using SendGrid", recipient); log.info("Successfully sent email to {} using SendGrid", recipient);
} else { } else {
throw new RuntimeException("Error sending via SendGrid to " + recipient + ": " + response.getMessage()); log.error("Failed to send email. Response code: {}, body: {}", response.getStatusCode(), response.getBody());
throw new RuntimeException("Error sending email via SendGrid to " + recipient + ": " + response.getBody());
} }
} catch (SendGridException e) { } catch (IOException e) {
throw new RuntimeException("Unable to send e-mail invite via SendGrid to " + recipient, e); log.error("IOException when sending email to {}: {}", recipient, e.getMessage(), e);
throw new RuntimeException("Unable to send email via SendGrid to " + recipient, e);
} }
} }
@Override
public void sendForInvite(IMatrixIdInvite invite) throws FeatureNotAvailable {
// Implementation...
}
@Override
public void sendForReply(IThreePidInviteReply reply) throws FeatureNotAvailable {
// Implementation...
}
@Override
public void sendForValidation(IThreePidSession session) throws FeatureNotAvailable {
// Implementation...
}
@Override
public void sendForUnbind(ThreePid pid) throws FeatureNotAvailable {
// Placeholder implementation
log.info("Unbind requested for: {}", pid.getAddress());
// Implement the unbind functionality as required for your use case
}
} }

View File

@@ -24,7 +24,7 @@ import io.kamax.matrix.ThreePid;
import io.kamax.mxisd.exception.BadRequestException; import io.kamax.mxisd.exception.BadRequestException;
import io.kamax.mxisd.exception.InvalidCredentialsException; import io.kamax.mxisd.exception.InvalidCredentialsException;
import io.kamax.mxisd.storage.dao.IThreePidSessionDao; import io.kamax.mxisd.storage.dao.IThreePidSessionDao;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.time.Instant; import java.time.Instant;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;

View File

@@ -8,7 +8,7 @@ import io.kamax.mxisd.config.rest.RestBackendConfig;
import io.kamax.mxisd.lookup.SingleLookupReply; 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 org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.http.entity.ContentType; import org.apache.http.entity.ContentType;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;

View File

@@ -37,7 +37,7 @@ import io.kamax.mxisd.invitation.ThreePidInviteReply;
import io.kamax.mxisd.threepid.connector.email.EmailSmtpConnector; import io.kamax.mxisd.threepid.connector.email.EmailSmtpConnector;
import io.kamax.mxisd.threepid.generator.PlaceholderNotificationGenerator; import io.kamax.mxisd.threepid.generator.PlaceholderNotificationGenerator;
import io.kamax.mxisd.threepid.session.ThreePidSession; import io.kamax.mxisd.threepid.session.ThreePidSession;
import org.apache.commons.lang.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;