Add LDAP support Matrix ID room invites notifications
This commit is contained in:
@@ -71,7 +71,6 @@ public class AppServiceHandler {
|
||||
String roomId = GsonUtil.getStringOrNull(ev, "room_id");
|
||||
_MatrixID sender = MatrixID.asAcceptable(GsonUtil.getStringOrNull(ev, "sender"));
|
||||
EventKey.StateKey.findString(ev).ifPresent(id -> {
|
||||
|
||||
_MatrixID mxid = MatrixID.asAcceptable(id);
|
||||
if (!StringUtils.equals(mxid.getDomain(), cfg.getDomain())) {
|
||||
log.debug("Ignoring invite for {}: not a local user");
|
||||
@@ -79,6 +78,7 @@ public class AppServiceHandler {
|
||||
}
|
||||
log.info("Got invite for {}", id);
|
||||
|
||||
boolean wasSent = false;
|
||||
for (_ThreePid tpid : profiler.getThreepids(mxid)) {
|
||||
if (!StringUtils.equals("email", tpid.getMedium())) {
|
||||
continue;
|
||||
@@ -86,12 +86,15 @@ public class AppServiceHandler {
|
||||
|
||||
log.info("Found an email address to notify about room invitation: {}", tpid.getAddress());
|
||||
Map<String, String> properties = new HashMap<>();
|
||||
profiler.getDisplayName(mxid).ifPresent(name -> properties.put("sender_display_name", name));
|
||||
profiler.getDisplayName(sender).ifPresent(name -> properties.put("sender_display_name", name));
|
||||
synapse.getRoomName(roomId).ifPresent(name -> properties.put("room_name", name));
|
||||
|
||||
IMatrixIdInvite inv = new MatrixIdInvite(roomId, sender, mxid, tpid.getMedium(), tpid.getAddress(), properties);
|
||||
notif.sendForInvite(inv);
|
||||
wasSent = true;
|
||||
}
|
||||
|
||||
log.info("Was notification sent? {}", wasSent);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2017 Maxime Dor
|
||||
* Copyright (C) 2017 Kamax Sarl
|
||||
*
|
||||
* https://max.kamax.io/
|
||||
* 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
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
package io.kamax.mxisd.backend.ldap;
|
||||
|
||||
import io.kamax.matrix._MatrixID;
|
||||
import io.kamax.mxisd.config.MatrixConfig;
|
||||
import io.kamax.mxisd.config.ldap.LdapConfig;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
@@ -70,7 +71,7 @@ public abstract class LdapBackend {
|
||||
return getAt().getUid().getValue();
|
||||
}
|
||||
|
||||
protected synchronized LdapConnection getConn() throws LdapException {
|
||||
protected synchronized LdapConnection getConn() {
|
||||
return new LdapNetworkConnection(cfg.getConnection().getHost(), cfg.getConnection().getPort(), cfg.getConnection().isTls());
|
||||
}
|
||||
|
||||
@@ -124,6 +125,17 @@ public abstract class LdapBackend {
|
||||
}
|
||||
}
|
||||
|
||||
public String buildUidFromMatrixId(_MatrixID mxId) {
|
||||
String uidType = getCfg().getAttribute().getUid().getType();
|
||||
if (StringUtils.equals(UID, uidType)) {
|
||||
return mxId.getLocalPart();
|
||||
} else if (StringUtils.equals(MATRIX_ID, uidType)) {
|
||||
return mxId.getId();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Bind type " + uidType + " is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<String> getAttribute(Entry entry, String attName) {
|
||||
Attribute attribute = entry.get(attName);
|
||||
if (attribute == null) {
|
||||
|
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2018 Kamax Sarl
|
||||
*
|
||||
* https://www.kamax.io/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.kamax.mxisd.backend.ldap;
|
||||
|
||||
import io.kamax.matrix.ThreePid;
|
||||
import io.kamax.matrix._MatrixID;
|
||||
import io.kamax.matrix._ThreePid;
|
||||
import io.kamax.mxisd.config.MatrixConfig;
|
||||
import io.kamax.mxisd.config.ldap.LdapConfig;
|
||||
import io.kamax.mxisd.exception.InternalServerError;
|
||||
import io.kamax.mxisd.profile.ProfileProvider;
|
||||
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.EntryCursor;
|
||||
import org.apache.directory.api.ldap.model.entry.Entry;
|
||||
import org.apache.directory.api.ldap.model.exception.LdapException;
|
||||
import org.apache.directory.api.ldap.model.message.SearchScope;
|
||||
import org.apache.directory.ldap.client.api.LdapConnection;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Component
|
||||
public class LdapProfileProvider extends LdapBackend implements ProfileProvider {
|
||||
|
||||
private transient Logger log = LoggerFactory.getLogger(LdapProfileProvider.class);
|
||||
|
||||
@Autowired
|
||||
public LdapProfileProvider(LdapConfig cfg, MatrixConfig mxCfg) {
|
||||
super(cfg, mxCfg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return getCfg().isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getDisplayName(_MatrixID userId) {
|
||||
try (LdapConnection conn = getConn()) {
|
||||
bind(conn);
|
||||
|
||||
String searchQuery = buildOrQueryWithFilter(getCfg().getProfile().getFilter(), buildUidFromMatrixId(userId), getUidAtt());
|
||||
|
||||
log.debug("Base DN: {}", getBaseDn());
|
||||
log.debug("Query: {}", searchQuery);
|
||||
|
||||
try (EntryCursor cursor = conn.search(getBaseDn(), searchQuery, SearchScope.SUBTREE, getAt().getName())) {
|
||||
while (cursor.next()) {
|
||||
Entry entry = cursor.get();
|
||||
log.info("Found possible match, DN: {}", entry.getDn().getName());
|
||||
Optional<String> v = getAttribute(entry, getAt().getName()).flatMap(uid -> {
|
||||
log.info("DN {} is a valid match", entry.getDn().getName());
|
||||
try {
|
||||
return getAttribute(entry, getAt().getName());
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn("Bind was found but type {} is not supported", getAt().getUid().getType());
|
||||
return Optional.empty();
|
||||
}
|
||||
});
|
||||
|
||||
if (v.isPresent()) {
|
||||
log.info("DN {} is the final match", entry.getDn().getName());
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (CursorLdapReferralException e) {
|
||||
log.warn("An entry is only available via referral, skipping");
|
||||
} catch (IOException | LdapException | CursorException e) {
|
||||
throw new InternalServerError(e);
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<_ThreePid> getThreepids(_MatrixID userId) {
|
||||
String uid = buildUidFromMatrixId(userId);
|
||||
log.info("Looking for display name of {}", uid);
|
||||
|
||||
List<_ThreePid> threePids = new ArrayList<>();
|
||||
try (LdapConnection conn = getConn()) {
|
||||
bind(conn);
|
||||
|
||||
log.debug("Base DN: {}", getBaseDn());
|
||||
getCfg().getAttribute().getThreepid().forEach((medium, attributes) -> {
|
||||
String[] attArray = new String[attributes.size()];
|
||||
attributes.toArray(attArray);
|
||||
|
||||
String searchQuery = buildOrQueryWithFilter(getCfg().getProfile().getFilter(), uid, getUidAtt());
|
||||
|
||||
log.debug("Query for 3PID {}: {}", medium, searchQuery);
|
||||
|
||||
try (EntryCursor cursor = conn.search(getBaseDn(), searchQuery, SearchScope.SUBTREE, attArray)) {
|
||||
while (cursor.next()) {
|
||||
Entry entry = cursor.get();
|
||||
log.info("Found possible match, DN: {}", entry.getDn().getName());
|
||||
try {
|
||||
attributes.stream()
|
||||
.flatMap(at -> getAttributes(entry, at).stream())
|
||||
.forEach(address -> {
|
||||
log.info("Found 3PID: {} - {}", medium, address);
|
||||
threePids.add(new ThreePid(medium, address));
|
||||
});
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn("Bind was found but type {} is not supported", getAt().getUid().getType());
|
||||
}
|
||||
}
|
||||
} catch (CursorLdapReferralException e) {
|
||||
log.warn("An entry is only available via referral, skipping");
|
||||
} catch (IOException | LdapException | CursorException e) {
|
||||
throw new InternalServerError(e);
|
||||
}
|
||||
});
|
||||
|
||||
} catch (IOException | LdapException e) {
|
||||
throw new InternalServerError(e);
|
||||
}
|
||||
|
||||
return threePids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRoles(_MatrixID userId) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2018 Kamax Sarl
|
||||
*
|
||||
* https://www.kamax.io/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.kamax.mxisd.backend.ldap.netiq;
|
||||
|
||||
import io.kamax.matrix._MatrixID;
|
||||
import io.kamax.mxisd.backend.ldap.LdapProfileProvider;
|
||||
import io.kamax.mxisd.config.MatrixConfig;
|
||||
import io.kamax.mxisd.config.ldap.LdapConfig;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class NetIqLdapProfileProvider extends LdapProfileProvider {
|
||||
|
||||
@Autowired
|
||||
public NetIqLdapProfileProvider(LdapConfig cfg, MatrixConfig mxCfg) {
|
||||
super(cfg, mxCfg);
|
||||
}
|
||||
|
||||
// FIXME this is duplicated in the other NetIQ classes, due to the Matrix ID generation code that was not abstracted
|
||||
@Override
|
||||
public String buildMatrixIdFromUid(String uid) {
|
||||
return super.buildMatrixIdFromUid(uid).toLowerCase();
|
||||
}
|
||||
|
||||
// FIXME this is duplicated in the other NetIQ classes, due to the Matrix ID generation code that was not abstracted
|
||||
@Override
|
||||
public String buildUidFromMatrixId(_MatrixID mxid) {
|
||||
return super.buildUidFromMatrixId(mxid).toLowerCase();
|
||||
}
|
||||
|
||||
}
|
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2017 Maxime Dor
|
||||
* Copyright (C) 2017 Kamax Sarl
|
||||
*
|
||||
* https://max.kamax.io/
|
||||
* 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
|
||||
@@ -234,6 +234,19 @@ public abstract class LdapConfig {
|
||||
|
||||
}
|
||||
|
||||
public static class Profile {
|
||||
|
||||
private String filter;
|
||||
|
||||
public String getFilter() {
|
||||
return filter;
|
||||
}
|
||||
|
||||
public void setFilter(String filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Logger log = LoggerFactory.getLogger(LdapConfig.class);
|
||||
|
||||
@@ -245,6 +258,7 @@ public abstract class LdapConfig {
|
||||
private Auth auth;
|
||||
private Directory directory;
|
||||
private Identity identity;
|
||||
private Profile profile = new Profile();
|
||||
|
||||
protected abstract String getConfigName();
|
||||
|
||||
@@ -304,6 +318,14 @@ public abstract class LdapConfig {
|
||||
this.identity = identity;
|
||||
}
|
||||
|
||||
public Profile getProfile() {
|
||||
return profile;
|
||||
}
|
||||
|
||||
public void setProfile(Profile profile) {
|
||||
this.profile = profile;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void build() {
|
||||
log.info("--- " + getConfigName() + " Config ---");
|
||||
@@ -356,6 +378,9 @@ public abstract class LdapConfig {
|
||||
getAuth().setFilter(StringUtils.defaultIfBlank(getAuth().getFilter(), getFilter()));
|
||||
getDirectory().setFilter(StringUtils.defaultIfBlank(getDirectory().getFilter(), getFilter()));
|
||||
getIdentity().setFilter(StringUtils.defaultIfBlank(getIdentity().getFilter(), getFilter()));
|
||||
if (StringUtils.isBlank(getProfile().getFilter())) {
|
||||
getProfile().setFilter(getFilter());
|
||||
}
|
||||
|
||||
log.info("Host: {}", connection.getHost());
|
||||
log.info("Port: {}", connection.getPort());
|
||||
@@ -367,6 +392,7 @@ public abstract class LdapConfig {
|
||||
log.info("Auth: {}", GsonUtil.get().toJson(auth));
|
||||
log.info("Directory: {}", GsonUtil.get().toJson(directory));
|
||||
log.info("Identity: {}", GsonUtil.get().toJson(identity));
|
||||
log.info("Profile: {}", GsonUtil.get().toJson(profile));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -36,7 +36,6 @@ import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.GET;
|
||||
@@ -44,7 +43,7 @@ import static org.springframework.web.bind.annotation.RequestMethod.PUT;
|
||||
|
||||
@RestController
|
||||
@CrossOrigin
|
||||
@RequestMapping(path = "/_matrix/app/v1", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public class AppServiceController {
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(AppServiceController.class);
|
||||
@@ -101,9 +100,11 @@ public class AppServiceController {
|
||||
List<JsonObject> events = GsonUtil.asList(GsonUtil.getArray(parser.parse(request.getInputStream()), "events"), JsonObject.class);
|
||||
handler.processTransaction(events);
|
||||
return "{}";
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (Throwable e) {
|
||||
log.warn("Unable to properly process transaction", e);
|
||||
}
|
||||
|
||||
return "{}";
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -67,8 +67,13 @@ public abstract class GenericTemplateNotificationGenerator extends PlaceholderNo
|
||||
|
||||
@Override
|
||||
public String getForInvite(IMatrixIdInvite invite) {
|
||||
String template = cfg.getGeneric().get("matrixId");
|
||||
if (StringUtils.isBlank(template)) {
|
||||
throw new InternalServerError("No " + invite.getMedium() + " template configured for Matrix ID invites");
|
||||
}
|
||||
|
||||
log.info("Generating notification content for Matrix ID invite");
|
||||
return populateForInvite(invite, getTemplateContent(cfg.getGeneric().get("matrixId")));
|
||||
return populateForInvite(invite, getTemplateContent(template));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Reference in New Issue
Block a user