Compare commits

..

6 Commits

Author SHA1 Message Date
Max Dor
3fc86465f8 Wordpress identity store (#67) 2018-03-23 17:14:59 +01:00
Max Dor
d93b546e3c Improved Travis-CI config
- Use default JDK
- Cache management for faster builds
2018-03-21 18:21:49 +01:00
Maxime Dor
ea15f24d41 Be clear about which LDAP config/backend is picked up 2018-03-21 02:32:00 +01:00
Max Dor
290a32d640 Merge pull request #66 from kamax-io/max/federation-discovery-enhancement
Better federation auto-discovery
2018-03-15 00:14:20 +01:00
Maxime Dor
10f9126cb6 Better federation auto-discovery
- Use the new status check endpoint at /_matrix/identity/api/v1
- Enforce DNS SRV existence before asking remote server for data
2018-03-11 18:28:48 +01:00
Maxime Dor
c3385b38dc Update to latest SDK 2018-03-09 23:58:16 +01:00
26 changed files with 875 additions and 81 deletions

View File

@@ -1,4 +1,8 @@
language: groovy
jdk:
- oraclejdk8
language: java
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
cache:
directories:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/

View File

@@ -80,7 +80,7 @@ dependencies {
compile "org.springframework.boot:spring-boot-starter-thymeleaf:1.5.10.RELEASE"
// Matrix Java SDK
compile 'io.kamax:matrix-java-sdk:0.0.2'
compile 'io.kamax:matrix-java-sdk:0.0.8'
// ed25519 handling
compile 'net.i2p.crypto:eddsa:0.1.0'
@@ -119,6 +119,9 @@ dependencies {
// PostgreSQL
compile 'org.postgresql:postgresql:42.1.4'
// MariaDB/MySQL
compile 'org.mariadb.jdbc:mariadb-java-client:2.1.2'
// Twilio SDK for SMS
compile 'com.twilio.sdk:twilio:7.14.5'

View File

@@ -17,6 +17,7 @@
- [SQL](backends/sql.md)
- [REST](backends/rest.md)
- [Google Firebase](backends/firebase.md)
- [Wordpress](backends/wordpress.md)
- Notifications
- Handlers
- [Basic](threepids/notifications/basic-handler.md)

View File

@@ -2,4 +2,5 @@
- [Samba / Active Directory / LDAP](ldap.md)
- [SQL Databases](sql.md)
- [Website / Web service / Web app](rest.md)
- [Google Firebase](firebase.md)
- [Google Firebase](firebase.md)
- [Wordpress](wordpress.md)

View File

@@ -0,0 +1,55 @@
# Wordpress
This Identity store allows you to use user accounts registered on your Wordpress setup.
Two types of connections are required for full support:
- [REST API](https://developer.wordpress.org/rest-api/) with JWT authentication
- Direct SQL access
This Identity store supports the following features:
- [Authentication](../features/authentication.md)
- [Directory](../features/directory-users.md)
- [Identity](../features/identity.md)
## Requirements
- [Wordpress](https://wordpress.org/download/) >= 4.4
- Permalink structure set to `Post Name`
- [JWT Auth plugin for REST API](https://wordpress.org/plugins/jwt-authentication-for-wp-rest-api/)
- SQL Credentials to the Wordpress Database
## Configuration
### Wordpress
#### JWT Auth
Set a JWT secret into `wp-config.php` like so:
```
define('JWT_AUTH_SECRET_KEY', 'your-top-secret-key');
```
`your-top-secret-key` should be set to a randomly generated value which is kept secret.
#### Rewrite of `index.php`
Wordpress is normally configured with rewrite of `index.php` so it does not appear in URLs.
If this is not the case for your installation, the mxisd URL will need to be appended with `/index.php`
### mxisd
Enable in the configuration:
```
wordpress.enabled: true
```
Configure the URL to your Wordpress installation - see above about added `/index.php`:
```
wordpress.rest.base: 'http://localhost:8080'
```
Configure the SQL connection to your Wordpress database:
```
wordpress.sql.connection: '//127.0.0.1/wordpress?user=root&password=example'
```
---
By default, MySQL database is expected. If you use another database, use:
```
wordpress.sql.type: 'jdbc-scheme'
```
With possible values:
- `mysql`
- `mariadb`
- `postgresql`
- `sqlite`

View File

@@ -115,8 +115,8 @@ Steps of user authentication using a 3PID:
- Homeserver
- Compatible Identity backends:
- LDAP
- SQL
- REST
- Wordpress
### Configuration
@@ -160,12 +160,3 @@ value is the base internal URL of the Homeserver, without any /_matrix/.. or tra
#### Backends
The Backends should be configured as described in the documentation of the [Directory User](directory-users.md) feature.

View File

@@ -219,6 +219,9 @@ For each query, `type` can be used to tell mxisd how to process the ID column:
#### REST
See the [dedicated document](../backends/rest.md)
#### Wordpress
See the [dedicated document](../backends/wordpress.md)
## Next steps
### Homeserver results
You can configure if the Homeserver should be queried at all when doing a directory search.

View File

@@ -143,3 +143,4 @@ Use your Identity stores:
- [SQL Database](backends/sql.md)
- [Website / Web service / Web app](backends/rest.md)
- [Google Firebase](backends/firebase.md)
- [Wordpress](backends/wordpress.md)

View File

@@ -52,7 +52,7 @@ public class AuthManager {
private InvitationManager invMgr;
public UserAuthResult authenticate(String id, String password) {
_MatrixID mxid = new MatrixID(id);
_MatrixID mxid = MatrixID.asAcceptable(id);
for (AuthenticatorProvider provider : providers) {
if (!provider.isEnabled()) {
continue;
@@ -63,9 +63,9 @@ public class AuthManager {
String mxId;
if (UserIdType.Localpart.is(result.getId().getType())) {
mxId = new MatrixID(result.getId().getValue(), mxCfg.getDomain()).getId();
mxId = MatrixID.from(result.getId().getValue(), mxCfg.getDomain()).acceptable().getId();
} else if (UserIdType.MatrixID.is(result.getId().getType())) {
mxId = new MatrixID(result.getId().getValue()).getId();
mxId = MatrixID.asAcceptable(result.getId().getValue()).getId();
} else {
log.warn("Unsupported User ID type {} for backend {}", result.getId().getType(), provider.getClass().getSimpleName());
continue;

View File

@@ -66,7 +66,6 @@ public class BackendAuthResult {
public void succeed(String id, String type, String displayName) {
this.success = true;
this.id = new UserID(type, id);
this.profile = new BackendAuthProfile();
this.profile.displayName = displayName;
}

View File

@@ -29,7 +29,7 @@ import io.kamax.mxisd.UserIdType;
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
import io.kamax.mxisd.auth.provider.BackendAuthResult;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.ldap.generic.GenericLdapConfig;
import io.kamax.mxisd.config.ldap.LdapConfig;
import io.kamax.mxisd.util.GsonUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.directory.api.ldap.model.cursor.CursorException;
@@ -59,7 +59,7 @@ public class LdapAuthProvider extends LdapBackend implements AuthenticatorProvid
private PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
@Autowired
public LdapAuthProvider(GenericLdapConfig cfg, MatrixConfig mxCfg) {
public LdapAuthProvider(LdapConfig cfg, MatrixConfig mxCfg) {
super(cfg, mxCfg);
}

View File

@@ -22,7 +22,6 @@ package io.kamax.mxisd.backend.ldap;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.ldap.LdapConfig;
import io.kamax.mxisd.config.ldap.generic.GenericLdapConfig;
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult;
import io.kamax.mxisd.directory.IDirectoryProvider;
import io.kamax.mxisd.exception.InternalServerError;
@@ -49,7 +48,7 @@ public class LdapDirectoryProvider extends LdapBackend implements IDirectoryProv
private Logger log = LoggerFactory.getLogger(LdapDirectoryProvider.class);
@Autowired
public LdapDirectoryProvider(GenericLdapConfig cfg, MatrixConfig mxCfg) {
public LdapDirectoryProvider(LdapConfig cfg, MatrixConfig mxCfg) {
super(cfg, mxCfg);
}

View File

@@ -21,7 +21,7 @@
package io.kamax.mxisd.backend.ldap;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.ldap.generic.GenericLdapConfig;
import io.kamax.mxisd.config.ldap.LdapConfig;
import io.kamax.mxisd.exception.InternalServerError;
import io.kamax.mxisd.lookup.SingleLookupReply;
import io.kamax.mxisd.lookup.SingleLookupRequest;
@@ -49,7 +49,7 @@ public class LdapThreePidProvider extends LdapBackend implements IThreePidProvid
private Logger log = LoggerFactory.getLogger(LdapThreePidProvider.class);
public LdapThreePidProvider(GenericLdapConfig cfg, MatrixConfig mxCfg) {
public LdapThreePidProvider(LdapConfig cfg, MatrixConfig mxCfg) {
super(cfg, mxCfg);
}

View File

@@ -0,0 +1,62 @@
/*
* 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.backend.wordpress;
public class WordpressAuthData {
public String token;
private String userEmail;
private String userNicename;
private String userDisplayName;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getUserEmail() {
return userEmail;
}
public void setUserEmail(String userEmail) {
this.userEmail = userEmail;
}
public String getUserNicename() {
return userNicename;
}
public void setUserNicename(String userNicename) {
this.userNicename = userNicename;
}
public String getUserDisplayName() {
return userDisplayName;
}
public void setUserDisplayName(String userDisplayName) {
this.userDisplayName = userDisplayName;
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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.backend.wordpress;
import io.kamax.matrix._MatrixID;
import io.kamax.mxisd.ThreePid;
import io.kamax.mxisd.UserIdType;
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
import io.kamax.mxisd.auth.provider.BackendAuthResult;
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;
@Component
public class WordpressAuthProvider implements AuthenticatorProvider {
private final Logger log = LoggerFactory.getLogger(WordpressAuthProvider.class);
private WordpressRestBackend wordpress;
@Autowired
public WordpressAuthProvider(WordpressRestBackend wordpress) {
this.wordpress = wordpress;
}
@Override
public boolean isEnabled() {
return wordpress.isEnabled();
}
@Override
public BackendAuthResult authenticate(_MatrixID mxid, String password) {
try {
WordpressAuthData data = wordpress.authenticate(mxid.getLocalPart(), password);
BackendAuthResult result = new BackendAuthResult();
if (StringUtils.isNotBlank(data.getUserEmail())) {
result.withThreePid(new ThreePid("email", data.getUserEmail()));
}
result.succeed(mxid.getId(), UserIdType.MatrixID.getId(), data.getUserDisplayName());
return result;
} catch (IllegalArgumentException e) {
log.error("Authentication failed for {}: {}", mxid.getId(), e.getMessage());
return BackendAuthResult.failure();
}
}
}

View File

@@ -0,0 +1,112 @@
/*
* 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.backend.wordpress;
import io.kamax.matrix.MatrixID;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.wordpress.WordpressConfig;
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult;
import io.kamax.mxisd.directory.IDirectoryProvider;
import io.kamax.mxisd.exception.InternalServerError;
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;
@Component
public class WordpressDirectoryProvider implements IDirectoryProvider {
private final Logger log = LoggerFactory.getLogger(WordpressDirectoryProvider.class);
private WordpressConfig cfg;
private WordressSqlBackend wordpress;
private MatrixConfig mxCfg;
@Autowired
public WordpressDirectoryProvider(WordpressConfig cfg, WordressSqlBackend wordpress, MatrixConfig mxCfg) {
this.cfg = cfg;
this.wordpress = wordpress;
this.mxCfg = mxCfg;
}
@Override
public boolean isEnabled() {
return wordpress.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<UserDirectorySearchResult.Result> processRow(ResultSet rSet) throws SQLException {
UserDirectorySearchResult.Result item = new UserDirectorySearchResult.Result();
item.setUserId(rSet.getString(1));
item.setDisplayName(rSet.getString(2));
return Optional.of(item);
}
public UserDirectorySearchResult search(String searchTerm, String query) {
try (Connection conn = wordpress.getConnection()) {
log.info("Will execute query: {}", query);
try (PreparedStatement stmt = conn.prepareStatement(query)) {
setParameters(stmt, searchTerm);
try (ResultSet rSet = stmt.executeQuery()) {
UserDirectorySearchResult result = new UserDirectorySearchResult();
result.setLimited(false);
while (rSet.next()) {
processRow(rSet).ifPresent(e -> {
e.setUserId(MatrixID.from(e.getUserId(), mxCfg.getDomain()).valid().getId());
result.addResult(e);
});
}
return result;
}
}
} catch (SQLException e) {
e.printStackTrace();
throw new InternalServerError(e);
}
}
@Override
public UserDirectorySearchResult searchByDisplayName(String searchTerm) {
log.info("Searching users by display name using '{}'", searchTerm);
return search(searchTerm, cfg.getSql().getQuery().getDirectory().get("name"));
}
@Override
public UserDirectorySearchResult searchBy3pid(String searchTerm) {
log.info("Searching users by 3PID using '{}'", searchTerm);
return search(searchTerm, cfg.getSql().getQuery().getDirectory().get("threepid"));
}
}

View File

@@ -0,0 +1,143 @@
/*
* 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.backend.wordpress;
import com.google.gson.JsonObject;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.matrix.json.InvalidJsonException;
import io.kamax.mxisd.config.wordpress.WordpressConfig;
import io.kamax.mxisd.util.RestClientUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class WordpressRestBackend {
private final Logger log = LoggerFactory.getLogger(WordpressRestBackend.class);
private final String jsonPath = "/wp-json";
private final String jwtPath = "/jwt-auth/v1";
private WordpressConfig cfg;
private CloseableHttpClient client;
private String jsonEndpoint;
private String jwtEndpoint;
private String token;
@Autowired
public WordpressRestBackend(WordpressConfig cfg, CloseableHttpClient client) {
this.cfg = cfg;
this.client = client;
if (!cfg.isEnabled()) {
return;
}
jsonEndpoint = cfg.getRest().getBase() + jsonPath;
jwtEndpoint = jsonEndpoint + jwtPath;
validateConfig();
}
private void validateConfig() {
log.info("Validating JWT auth endpoint");
try (CloseableHttpResponse res = client.execute(new HttpGet(jwtEndpoint))) {
int status = res.getStatusLine().getStatusCode();
if (status != 200) {
log.warn("JWT auth endpoint check failed: Got status code {}", status);
return;
}
String data = EntityUtils.toString(res.getEntity());
if (StringUtils.isBlank(data)) {
log.warn("JWT auth endpoint check failed: Got no/empty body data");
}
JsonObject body = GsonUtil.parseObj(data);
if (!body.has("namespace")) {
log.warn("JWT auth endpoint check failed: invalid namespace");
}
log.info("JWT auth endpoint check succeeded");
} catch (InvalidJsonException e) {
log.warn("JWT auth endpoint check failed: Invalid JSON response: {}", e.getMessage());
} catch (IOException e) {
log.warn("JWT auth endpoint check failed: Could not read API endpoint: {}", e.getMessage());
}
}
public boolean isEnabled() {
return cfg.isEnabled();
}
protected WordpressAuthData authenticate(String username, String password) {
JsonObject body = new JsonObject();
body.addProperty("username", username);
body.addProperty("password", password);
HttpPost req = RestClientUtils.post(jwtEndpoint + "/token", body);
try (CloseableHttpResponse res = client.execute(req)) {
int status = res.getStatusLine().getStatusCode();
String bodyRes = EntityUtils.toString(res.getEntity());
if (status != 200) {
throw new IllegalArgumentException(bodyRes);
}
return GsonUtil.get().fromJson(bodyRes, WordpressAuthData.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
protected void authenticate() {
WordpressAuthData data = authenticate(
cfg.getRest().getCredential().getUsername(),
cfg.getRest().getCredential().getPassword());
log.info("Internal authentication: success, logged in as " + data.getUserNicename());
token = data.getToken();
}
protected CloseableHttpResponse runRequest(HttpRequestBase request) throws IOException {
request.setHeader("Authorization", "Bearer " + token);
return client.execute(request);
}
public CloseableHttpResponse withAuthentication(HttpRequestBase request) throws IOException {
CloseableHttpResponse response = runRequest(request);
if (response.getStatusLine().getStatusCode() == 403) { //FIXME we should check the JWT expiration time
authenticate();
response = runRequest(request);
}
return response;
}
}

View File

@@ -0,0 +1,114 @@
/*
* 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.backend.wordpress;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix._MatrixID;
import io.kamax.mxisd.ThreePid;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.wordpress.WordpressConfig;
import io.kamax.mxisd.lookup.SingleLookupReply;
import io.kamax.mxisd.lookup.SingleLookupRequest;
import io.kamax.mxisd.lookup.ThreePidMapping;
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
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.List;
import java.util.Objects;
import java.util.Optional;
@Component
public class WordpressThreePidProvider implements IThreePidProvider {
private final Logger log = LoggerFactory.getLogger(WordpressThreePidProvider.class);
private MatrixConfig mxCfg;
private WordpressConfig cfg;
private WordressSqlBackend wordpress;
@Autowired
public WordpressThreePidProvider(MatrixConfig mxCfg, WordpressConfig cfg, WordressSqlBackend wordpress) {
this.mxCfg = mxCfg;
this.cfg = cfg;
this.wordpress = wordpress;
}
@Override
public boolean isEnabled() {
return wordpress.isEnabled();
}
@Override
public boolean isLocal() {
return true;
}
@Override
public int getPriority() {
return 15;
}
protected Optional<_MatrixID> find(ThreePid tpid) {
String query = cfg.getSql().getQuery().getThreepid().get(tpid.getMedium());
if (Objects.isNull(query)) {
return Optional.empty();
}
try (Connection conn = wordpress.getConnection()) {
PreparedStatement stmt = conn.prepareStatement(query);
stmt.setString(1, tpid.getAddress());
try (ResultSet rSet = stmt.executeQuery()) {
while (rSet.next()) {
String uid = rSet.getString("uid");
log.info("Found match: {}", uid);
return Optional.of(MatrixID.from(uid, mxCfg.getDomain()).valid());
}
log.info("No match found in Wordpress");
return Optional.empty();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public Optional<SingleLookupReply> find(SingleLookupRequest request) {
return find(new ThreePid(request.getType(), request.getThreePid())).map(mxid -> new SingleLookupReply(request, mxid));
}
@Override
public List<ThreePidMapping> populate(List<ThreePidMapping> mappings) {
for (ThreePidMapping tpidMap : mappings) {
find(new ThreePid(tpidMap.getMedium(), tpidMap.getValue())).ifPresent(mxid -> tpidMap.setMxid(mxid.getId()));
}
return mappings;
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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.backend.wordpress;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import io.kamax.mxisd.config.wordpress.WordpressConfig;
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.SQLException;
@Component
public class WordressSqlBackend {
private Logger log = LoggerFactory.getLogger(WordressSqlBackend.class);
private WordpressConfig cfg;
private ComboPooledDataSource ds;
@Autowired
public WordressSqlBackend(WordpressConfig cfg) {
this.cfg = cfg;
ds = new ComboPooledDataSource();
ds.setJdbcUrl("jdbc:" + cfg.getSql().getType() + ":" + cfg.getSql().getConnection());
ds.setMinPoolSize(1);
ds.setMaxPoolSize(10);
ds.setAcquireIncrement(2);
}
public boolean isEnabled() {
return cfg.isEnabled();
}
public Connection getConnection() throws SQLException {
return ds.getConnection();
}
}

View File

@@ -20,22 +20,18 @@
package io.kamax.mxisd.config.ldap;
import com.google.gson.Gson;
import io.kamax.matrix.ThreePidMedium;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.backend.ldap.LdapBackend;
import io.kamax.mxisd.exception.ConfigurationException;
import org.apache.commons.lang.StringUtils;
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.*;
@Configuration
@ConfigurationProperties(prefix = "ldap")
public class LdapConfig {
public abstract class LdapConfig {
public static class UID {
@@ -240,7 +236,6 @@ public class LdapConfig {
private Logger log = LoggerFactory.getLogger(LdapConfig.class);
private static Gson gson = new Gson();
private boolean enabled;
private String filter;
@@ -251,6 +246,8 @@ public class LdapConfig {
private Directory directory;
private Identity identity;
protected abstract String getConfigName();
public boolean isEnabled() {
return enabled;
}
@@ -309,7 +306,7 @@ public class LdapConfig {
@PostConstruct
public void build() {
log.info("--- LDAP Config ---");
log.info("--- " + getConfigName() + " Config ---");
log.info("Enabled: {}", isEnabled());
if (!isEnabled()) {
@@ -365,10 +362,10 @@ public class LdapConfig {
log.info("Bind DN: {}", connection.getBindDn());
log.info("Base DN: {}", connection.getBaseDn());
log.info("Attribute: {}", gson.toJson(attribute));
log.info("Auth: {}", gson.toJson(auth));
log.info("Directory: {}", gson.toJson(directory));
log.info("Identity: {}", gson.toJson(identity));
log.info("Attribute: {}", GsonUtil.get().toJson(attribute));
log.info("Auth: {}", GsonUtil.get().toJson(auth));
log.info("Directory: {}", GsonUtil.get().toJson(directory));
log.info("Identity: {}", GsonUtil.get().toJson(identity));
}
}

View File

@@ -30,4 +30,9 @@ import org.springframework.context.annotation.Primary;
@Primary
public class GenericLdapConfig extends LdapConfig {
@Override
protected String getConfigName() {
return "Generic LDAP";
}
}

View File

@@ -20,12 +20,17 @@
package io.kamax.mxisd.config.ldap.netiq;
import io.kamax.mxisd.config.ldap.generic.GenericLdapConfig;
import io.kamax.mxisd.config.ldap.LdapConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "netiq")
public class NetIqLdapConfig extends GenericLdapConfig {
public class NetIqLdapConfig extends LdapConfig {
@Override
protected String getConfigName() {
return "NetIQ eDirectory";
}
}

View File

@@ -0,0 +1,175 @@
/*
* 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.wordpress;
import io.kamax.mxisd.exception.ConfigurationException;
import org.apache.commons.lang.StringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import java.util.Map;
@Configuration
@ConfigurationProperties("wordpress")
public class WordpressConfig {
public static class Credential {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
public static class Rest {
private Credential credential = new Credential();
private String base;
public String getBase() {
return base;
}
public void setBase(String base) {
this.base = base;
}
public Credential getCredential() {
return credential;
}
public void setCredential(Credential credential) {
this.credential = credential;
}
}
public static class Query {
private Map<String, String> threepid;
private Map<String, String> directory;
public Map<String, String> getThreepid() {
return threepid;
}
public void setThreepid(Map<String, String> threepid) {
this.threepid = threepid;
}
public Map<String, String> getDirectory() {
return directory;
}
public void setDirectory(Map<String, String> directory) {
this.directory = directory;
}
}
public static class Sql {
private String type;
private String connection;
private Query query;
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 Query getQuery() {
return query;
}
public void setQuery(Query query) {
this.query = query;
}
}
private boolean enabled;
private Rest rest = new Rest();
private Sql sql = new Sql();
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Rest getRest() {
return rest;
}
public void setRest(Rest rest) {
this.rest = rest;
}
public Sql getSql() {
return sql;
}
public void setSql(Sql sql) {
this.sql = sql;
}
@PostConstruct
public void build() {
if (!isEnabled()) {
return;
}
if (StringUtils.isBlank(getRest().getBase())) {
throw new ConfigurationException("wordpress.rest.base");
}
}
}

View File

@@ -22,9 +22,6 @@ import java.util.Optional;
// FIXME placeholder, this must go in matrix-java-sdk for 1.0
public class IdentityServerUtils {
public static final String THREEPID_TEST_MEDIUM = "email";
public static final String THREEPID_TEST_ADDRESS = "mxisd-email-forever-unknown@forever-invalid.kamax.io";
private static Logger log = LoggerFactory.getLogger(IdentityServerUtils.class);
private static JsonParser parser = new JsonParser();
@@ -35,9 +32,7 @@ public class IdentityServerUtils {
try {
// FIXME use Apache HTTP client
HttpURLConnection rootSrvConn = (HttpURLConnection) new URL(
remote + "/_matrix/identity/api/v1/lookup?medium=" + THREEPID_TEST_MEDIUM + "&address=" + THREEPID_TEST_ADDRESS
).openConnection();
HttpURLConnection rootSrvConn = (HttpURLConnection) new URL(remote + "/_matrix/identity/api/v1/").openConnection();
// TODO turn this into a configuration property
rootSrvConn.setConnectTimeout(2000);
@@ -53,11 +48,6 @@ public class IdentityServerUtils {
return false;
}
if (el.getAsJsonObject().has("address")) {
log.debug("IS {} did not send back a JSON object for single 3PID lookup");
return false;
}
return true;
} catch (IllegalArgumentException | IOException | JsonParseException e) {
log.info("{} is not a usable Identity Server: {}", remote, e.getMessage());
@@ -84,39 +74,35 @@ public class IdentityServerUtils {
List<SRVRecord> srvRecords = new ArrayList<>();
Record[] records = new Lookup(lookupDns, Type.SRV).run();
if (records != null) {
for (Record record : records) {
log.info("Record: {}", record.toString());
if (record.getType() == Type.SRV) {
if (record instanceof SRVRecord) {
srvRecords.add((SRVRecord) record);
} else {
log.warn("We requested SRV records but we got {} instead!", record.getClass().getName());
}
} else {
log.warn("We request SRV type records but we got type #{} instead!", record.getType());
}
}
srvRecords.sort(Comparator.comparingInt(SRVRecord::getPriority));
if (records == null || records.length == 0) {
log.info("No SRV record for {}", lookupDns);
return Optional.empty();
}
for (SRVRecord srvRecord : srvRecords) {
String baseUrl = "https://" + srvRecord.getTarget().toString(true) + ":" + srvRecord.getPort();
for (Record record : records) {
log.info("Record: {}", record.toString());
if (record.getType() == Type.SRV) {
if (record instanceof SRVRecord) {
srvRecords.add((SRVRecord) record);
} else {
log.warn("We requested SRV records but we got {} instead!", record.getClass().getName());
}
} else {
log.warn("We request SRV type records but we got type #{} instead!", record.getType());
}
}
srvRecords.sort(Comparator.comparingInt(SRVRecord::getPriority));
for (SRVRecord srvRecord : srvRecords) {
String baseUrl = "https://" + srvRecord.getTarget().toString(true) + ":" + srvRecord.getPort();
if (isUsable(baseUrl)) {
log.info("Found Identity Server for domain {} at {}", domainOrUrl, baseUrl);
return Optional.of(baseUrl);
}
} else {
log.info("No SRV record for {}", lookupDns);
}
log.info("Performing basic lookup using domain name {}", domainOrUrl);
String baseUrl = "https://" + domainOrUrl;
if (isUsable(baseUrl)) {
log.info("Found Identity Server for domain {} at {}", domainOrUrl, baseUrl);
return Optional.of(baseUrl);
} else {
log.info("{} is not a usable Identity Server", baseUrl);
return Optional.empty();
}
log.info("Found no Identity server for domain {} at {}");
return Optional.empty();
} catch (TextParseException e) {
log.warn(domainOrUrl + " is not a valid domain name");
return Optional.empty();

View File

@@ -20,9 +20,7 @@
package io.kamax.mxisd.util;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
@@ -31,7 +29,7 @@ import java.nio.charset.StandardCharsets;
public class RestClientUtils {
private static Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
private static Gson gson = GsonUtil.build();
public static HttpPost post(String url, String body) {
StringEntity entity = new StringEntity(body, StandardCharsets.UTF_8);

View File

@@ -156,6 +156,17 @@ synapseSql:
enabled: false
type: 'sqlite'
wordpress:
enabled: false
sql:
type: 'mysql'
query:
threepid:
email: 'SELECT user_login as uid FROM wp_users WHERE user_email = ?'
directory:
name: "SELECT DISTINCT user_login, display_name FROM wp_users u LEFT JOIN wp_usermeta m ON m.user_id = u.id WHERE u.display_name LIKE ? OR (m.meta_key = 'nickname' AND m.meta_value = ?) OR (m.meta_key = 'first_name' AND m.meta_value = ?) OR (m.meta_key = 'last_name' AND m.meta_value = ?);"
threepid: 'SELECT DISTINCT user_login, display_name FROM wp_users WHERE user_email LIKE ?'
forward:
servers:
- 'https://matrix.org'