diff --git a/application.example.yaml b/application.example.yaml
index 4e34371..13d33a9 100644
--- a/application.example.yaml
+++ b/application.example.yaml
@@ -223,6 +223,21 @@ key.path: '/path/to/sign.key'
#ldap.identity.medium.msisdn: "(|(telephoneNumber=+%3pid)(mobile=+%3pid)(homePhone=+%3pid)(otherTelephone=+%3pid)(otherMobile=+%3pid)(otherHomePhone=+%3pid))"
+############################
+# SQL Provider config item #
+############################
+#
+# Example configuration to integrate with synapse SQLite DB (default configuration)
+#
+#sql.enabled: true
+#sql.type: 'sqlite'
+#sql.connection: '/var/lib/matrix-synapse/homeserver.db'
+#sql.identity.type: 'mxid'
+#sql.identity.query: 'SELECT user_id AS uid FROM user_threepids WHERE medium = ? AND address = ?'
+#sql.identity.medium.email: "SELECT user_id AS uid FROM user_threepids WHERE medium = ? AND address = ?"
+
+
+
#######################################
# Lookup queries forward config items #
#######################################
diff --git a/src/main/groovy/io/kamax/mxisd/auth/provider/SqlAuthProvider.java b/src/main/groovy/io/kamax/mxisd/auth/provider/SqlAuthProvider.java
new file mode 100644
index 0000000..28b41d7
--- /dev/null
+++ b/src/main/groovy/io/kamax/mxisd/auth/provider/SqlAuthProvider.java
@@ -0,0 +1,59 @@
+/*
+ * 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 .
+ */
+
+package io.kamax.mxisd.auth.provider;
+
+import io.kamax.mxisd.auth.UserAuthResult;
+import io.kamax.mxisd.config.ServerConfig;
+import io.kamax.mxisd.config.sql.SqlProviderConfig;
+import io.kamax.mxisd.invitation.InvitationManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class SqlAuthProvider implements AuthenticatorProvider {
+
+ private Logger log = LoggerFactory.getLogger(SqlAuthProvider.class);
+
+ @Autowired
+ private ServerConfig srvCfg;
+
+ @Autowired
+ private SqlProviderConfig cfg;
+
+ @Autowired
+ private InvitationManager invMgr;
+
+ @Override
+ public boolean isEnabled() {
+ return cfg.isEnabled();
+ }
+
+ @Override
+ public UserAuthResult authenticate(String id, String password) {
+ log.info("Performing dummy authentication try to force invite mapping refresh");
+
+ invMgr.lookupMappingsForInvites();
+ return new UserAuthResult().failure();
+ }
+
+}
diff --git a/src/main/groovy/io/kamax/mxisd/config/sql/SqlProviderAuthConfig.java b/src/main/groovy/io/kamax/mxisd/config/sql/SqlProviderAuthConfig.java
new file mode 100644
index 0000000..4d826a7
--- /dev/null
+++ b/src/main/groovy/io/kamax/mxisd/config/sql/SqlProviderAuthConfig.java
@@ -0,0 +1,21 @@
+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;
+ }
+
+}
diff --git a/src/main/groovy/io/kamax/mxisd/config/sql/SqlProviderConfig.java b/src/main/groovy/io/kamax/mxisd/config/sql/SqlProviderConfig.java
new file mode 100644
index 0000000..2607ad0
--- /dev/null
+++ b/src/main/groovy/io/kamax/mxisd/config/sql/SqlProviderConfig.java
@@ -0,0 +1,96 @@
+/*
+ * 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 .
+ */
+
+package io.kamax.mxisd.config.sql;
+
+import com.google.gson.Gson;
+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;
+
+@Configuration
+@ConfigurationProperties("sql")
+public class SqlProviderConfig {
+
+ private Logger log = LoggerFactory.getLogger(SqlProviderConfig.class);
+
+ private boolean enabled;
+ private String type;
+ private String connection;
+ private SqlProviderAuthConfig auth;
+ private SqlProviderIdentityConfig identity;
+
+ 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 getConnection() {
+ return connection;
+ }
+
+ public void setConnection(String connection) {
+ this.connection = connection;
+ }
+
+ public SqlProviderAuthConfig getAuth() {
+ return auth;
+ }
+
+ public void setAuth(SqlProviderAuthConfig auth) {
+ this.auth = auth;
+ }
+
+ public SqlProviderIdentityConfig getIdentity() {
+ return identity;
+ }
+
+ public void setIdentity(SqlProviderIdentityConfig identity) {
+ this.identity = identity;
+ }
+
+ @PostConstruct
+ private void postConstruct() {
+ log.info("--- SQL Provider config ---");
+ log.info("Enabled: {}", isEnabled());
+ if (isEnabled()) {
+ log.info("Type: {}", getType());
+ log.info("Connection: {}", getConnection());
+ log.info("Auth enabled: {}", getAuth().isEnabled());
+ log.info("Identy type: {}", getIdentity().getType());
+ log.info("Identity medium queries: {}", new Gson().toJson(getIdentity().getMedium()));
+ }
+ }
+
+}
diff --git a/src/main/groovy/io/kamax/mxisd/config/sql/SqlProviderIdentityConfig.java b/src/main/groovy/io/kamax/mxisd/config/sql/SqlProviderIdentityConfig.java
new file mode 100644
index 0000000..c55c662
--- /dev/null
+++ b/src/main/groovy/io/kamax/mxisd/config/sql/SqlProviderIdentityConfig.java
@@ -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 .
+ */
+
+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 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 getMedium() {
+ return medium;
+ }
+
+ public void setMedium(Map medium) {
+ this.medium = medium;
+ }
+
+}
diff --git a/src/main/groovy/io/kamax/mxisd/controller/v1/SessionController.groovy b/src/main/groovy/io/kamax/mxisd/controller/v1/SessionController.groovy
index 41aafcd..4e430e6 100644
--- a/src/main/groovy/io/kamax/mxisd/controller/v1/SessionController.groovy
+++ b/src/main/groovy/io/kamax/mxisd/controller/v1/SessionController.groovy
@@ -25,6 +25,7 @@ import com.google.gson.JsonObject
import io.kamax.mxisd.controller.v1.io.SessionEmailTokenRequestJson
import io.kamax.mxisd.controller.v1.io.SessionPhoneTokenRequestJson
import io.kamax.mxisd.exception.BadRequestException
+import io.kamax.mxisd.invitation.InvitationManager
import io.kamax.mxisd.lookup.ThreePidValidation
import io.kamax.mxisd.mapping.MappingManager
import org.apache.commons.io.IOUtils
@@ -48,6 +49,9 @@ class SessionController {
@Autowired
private MappingManager mgr
+ @Autowired
+ private InvitationManager invMgr;
+
private Gson gson = new Gson()
private Logger log = LoggerFactory.getLogger(SessionController.class)
@@ -131,6 +135,10 @@ class SessionController {
obj.addProperty("error", e.getMessage())
response.setStatus(HttpStatus.SC_BAD_REQUEST)
return gson.toJson(obj)
+ } finally {
+ // If a user registers, there is no standard login event. Instead, this is the only way to trigger
+ // resolution at an appropriate time. Meh at synapse/Riot!
+ invMgr.lookupMappingsForInvites()
}
}
diff --git a/src/main/groovy/io/kamax/mxisd/invitation/InvitationManager.java b/src/main/groovy/io/kamax/mxisd/invitation/InvitationManager.java
index 4d7d683..96a0846 100644
--- a/src/main/groovy/io/kamax/mxisd/invitation/InvitationManager.java
+++ b/src/main/groovy/io/kamax/mxisd/invitation/InvitationManager.java
@@ -85,7 +85,7 @@ public class InvitationManager {
private Timer refreshTimer;
private String getId(IThreePidInvite invite) {
- return invite.getSender().getDomain() + invite.getMedium() + invite.getAddress();
+ return invite.getSender().getDomain().toLowerCase() + invite.getMedium().toLowerCase() + invite.getAddress().toLowerCase();
}
@PostConstruct
@@ -233,7 +233,7 @@ public class InvitationManager {
public void publishMappingIfInvited(ThreePidMapping threePid) {
log.info("Looking up possible pending invites for {}:{}", threePid.getMedium(), threePid.getValue());
for (IThreePidInviteReply reply : invitations.values()) {
- if (StringUtils.equals(reply.getInvite().getMedium(), threePid.getMedium()) && StringUtils.equals(reply.getInvite().getAddress(), threePid.getValue())) {
+ if (StringUtils.equalsIgnoreCase(reply.getInvite().getMedium(), threePid.getMedium()) && StringUtils.equalsIgnoreCase(reply.getInvite().getAddress(), threePid.getValue())) {
log.info("{}:{} has an invite pending on HS {}, publishing mapping", threePid.getMedium(), threePid.getValue(), reply.getInvite().getSender().getDomain());
publishMapping(reply, threePid.getMxid());
}
diff --git a/src/main/groovy/io/kamax/mxisd/lookup/provider/SqlProvider.java b/src/main/groovy/io/kamax/mxisd/lookup/provider/SqlProvider.java
new file mode 100644
index 0000000..c7b7225
--- /dev/null
+++ b/src/main/groovy/io/kamax/mxisd/lookup/provider/SqlProvider.java
@@ -0,0 +1,107 @@
+/*
+ * 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 .
+ */
+
+package io.kamax.mxisd.lookup.provider;
+
+import io.kamax.matrix.MatrixID;
+import io.kamax.mxisd.config.ServerConfig;
+import io.kamax.mxisd.config.sql.SqlProviderConfig;
+import io.kamax.mxisd.lookup.SingleLookupReply;
+import io.kamax.mxisd.lookup.SingleLookupRequest;
+import io.kamax.mxisd.lookup.ThreePidMapping;
+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.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+@Component
+public class SqlProvider implements IThreePidProvider {
+
+ private Logger log = LoggerFactory.getLogger(SqlProvider.class);
+
+ @Autowired
+ private ServerConfig srvCfg;
+
+ @Autowired
+ private SqlProviderConfig cfg;
+
+ @Override
+ public boolean isEnabled() {
+ return cfg.isEnabled();
+ }
+
+ @Override
+ public boolean isLocal() {
+ return true;
+ }
+
+ @Override
+ public int getPriority() {
+ return 20;
+ }
+
+ private Connection getConn() throws SQLException {
+ return DriverManager.getConnection("jdbc:" + cfg.getType() + ":" + cfg.getConnection());
+ }
+
+ @Override
+ public Optional find(SingleLookupRequest request) {
+ log.info("SQL lookup");
+ String stmtSql = StringUtils.defaultIfBlank(cfg.getIdentity().getMedium().get(request.getType()), cfg.getIdentity().getQuery());
+ log.info("SQL query: {}", stmtSql);
+ try (PreparedStatement stmt = getConn().prepareStatement(stmtSql)) {
+ stmt.setString(1, request.getType().toLowerCase());
+ stmt.setString(2, request.getThreePid().toLowerCase());
+
+ ResultSet rSet = stmt.executeQuery();
+ while (rSet.next()) {
+ String uid = rSet.getString("uid");
+ log.info("Found match: {}", uid);
+ if (StringUtils.equals("uid", cfg.getIdentity().getType())) {
+ log.info("Resolving as localpart");
+ return Optional.of(new SingleLookupReply(request, new MatrixID(uid, srvCfg.getName())));
+ }
+ if (StringUtils.equals("mxid", cfg.getIdentity().getType())) {
+ log.info("Resolving as MXID");
+ return Optional.of(new SingleLookupReply(request, new MatrixID(uid)));
+ }
+
+ log.info("Identity type is unknown, skipping");
+ }
+
+ log.info("No match found in SQL");
+ return Optional.empty();
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public List populate(List mappings) {
+ return new ArrayList<>();
+ }
+
+}