LDAP Directory search support
This commit is contained in:
@@ -24,6 +24,8 @@ 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 io.kamax.mxisd.config.MatrixConfig;
|
||||||
|
import io.kamax.mxisd.config.ldap.LdapConfig;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.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;
|
||||||
@@ -35,6 +37,7 @@ import org.apache.directory.api.ldap.model.message.SearchScope;
|
|||||||
import org.apache.directory.ldap.client.api.LdapConnection;
|
import org.apache.directory.ldap.client.api.LdapConnection;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -44,8 +47,9 @@ public class LdapAuthProvider extends LdapGenericBackend implements Authenticato
|
|||||||
|
|
||||||
private Logger log = LoggerFactory.getLogger(LdapAuthProvider.class);
|
private Logger log = LoggerFactory.getLogger(LdapAuthProvider.class);
|
||||||
|
|
||||||
private String getUidAttribute() {
|
@Autowired
|
||||||
return getCfg().getAttribute().getUid().getValue();
|
public LdapAuthProvider(LdapConfig cfg, MatrixConfig mxCfg) {
|
||||||
|
super(cfg, mxCfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -57,37 +61,34 @@ public class LdapAuthProvider extends LdapGenericBackend implements Authenticato
|
|||||||
public BackendAuthResult authenticate(_MatrixID mxid, String password) {
|
public BackendAuthResult authenticate(_MatrixID mxid, String password) {
|
||||||
log.info("Performing auth for {}", mxid);
|
log.info("Performing auth for {}", mxid);
|
||||||
|
|
||||||
LdapConnection conn = getConn();
|
|
||||||
try {
|
try (LdapConnection conn = getConn()) {
|
||||||
bind(conn);
|
bind(conn);
|
||||||
|
|
||||||
String uidType = getCfg().getAttribute().getUid().getType();
|
String uidType = getAt().getUid().getType();
|
||||||
String userFilterValue = StringUtils.equals(LdapThreePidProvider.UID, uidType) ? mxid.getLocalPart() : mxid.getId();
|
String userFilterValue = StringUtils.equals(LdapGenericBackend.UID, uidType) ? mxid.getLocalPart() : mxid.getId();
|
||||||
if (StringUtils.isBlank(userFilterValue)) {
|
if (StringUtils.isBlank(userFilterValue)) {
|
||||||
log.warn("Username is empty, failing auth");
|
log.warn("Username is empty, failing auth");
|
||||||
return BackendAuthResult.failure();
|
return BackendAuthResult.failure();
|
||||||
}
|
}
|
||||||
|
|
||||||
String userFilter = "(" + getCfg().getAttribute().getUid().getValue() + "=" + userFilterValue + ")";
|
String userFilter = "(" + getCfg().getAttribute().getUid().getValue() + "=" + userFilterValue + ")";
|
||||||
if (!StringUtils.isBlank(getCfg().getAuth().getFilter())) {
|
userFilter = buildWithFilter(userFilter, getCfg().getAuth().getFilter());
|
||||||
userFilter = "(&" + getCfg().getAuth().getFilter() + userFilter + ")";
|
try (EntryCursor cursor = conn.search(getBaseDn(), userFilter, SearchScope.SUBTREE, getUidAtt(), getAt().getName())) {
|
||||||
}
|
|
||||||
EntryCursor cursor = conn.search(getCfg().getConn().getBaseDn(), userFilter, SearchScope.SUBTREE, getUidAttribute(), getCfg().getAttribute().getName());
|
|
||||||
try {
|
|
||||||
while (cursor.next()) {
|
while (cursor.next()) {
|
||||||
Entry entry = cursor.get();
|
Entry entry = cursor.get();
|
||||||
String dn = entry.getDn().getName();
|
String dn = entry.getDn().getName();
|
||||||
log.info("Checking possible match, DN: {}", dn);
|
log.info("Checking possible match, DN: {}", dn);
|
||||||
|
|
||||||
Attribute attribute = entry.get(getUidAttribute());
|
Attribute attribute = entry.get(getUidAtt());
|
||||||
if (attribute == null) {
|
if (attribute == null) {
|
||||||
log.info("DN {}: no attribute {}, skpping", dn, getUidAttribute());
|
log.info("DN {}: no attribute {}, skpping", dn, getUidAtt());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
String data = attribute.get().toString();
|
String data = attribute.get().toString();
|
||||||
if (data.length() < 1) {
|
if (data.length() < 1) {
|
||||||
log.info("DN {}: empty attribute {}, skipping", getUidAttribute());
|
log.info("DN {}: empty attribute {}, skipping", getUidAtt());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +100,7 @@ public class LdapAuthProvider extends LdapGenericBackend implements Authenticato
|
|||||||
return BackendAuthResult.failure();
|
return BackendAuthResult.failure();
|
||||||
}
|
}
|
||||||
|
|
||||||
Attribute nameAttribute = entry.get(getCfg().getAttribute().getName());
|
Attribute nameAttribute = entry.get(getAt().getName());
|
||||||
String name = nameAttribute != null ? nameAttribute.get().toString() : null;
|
String name = nameAttribute != null ? nameAttribute.get().toString() : null;
|
||||||
|
|
||||||
log.info("Authentication successful for {}", entry.getDn().getName());
|
log.info("Authentication successful for {}", entry.getDn().getName());
|
||||||
@@ -110,20 +111,12 @@ public class LdapAuthProvider extends LdapGenericBackend implements Authenticato
|
|||||||
}
|
}
|
||||||
} catch (CursorLdapReferralException e) {
|
} catch (CursorLdapReferralException e) {
|
||||||
log.warn("Entity for {} is only available via referral, skipping", mxid);
|
log.warn("Entity for {} is only available via referral, skipping", mxid);
|
||||||
} finally {
|
|
||||||
cursor.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("No match were found for {}", mxid);
|
log.info("No match were found for {}", mxid);
|
||||||
return BackendAuthResult.failure();
|
return BackendAuthResult.failure();
|
||||||
} catch (LdapException | IOException | CursorException e) {
|
} catch (LdapException | IOException | CursorException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
conn.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* mxisd - Matrix Identity Server Daemon
|
||||||
|
* Copyright (C) 2017 Maxime Dor
|
||||||
|
*
|
||||||
|
* https://max.kamax.io/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.kamax.mxisd.backend.ldap;
|
||||||
|
|
||||||
|
import io.kamax.mxisd.config.MatrixConfig;
|
||||||
|
import io.kamax.mxisd.config.ldap.LdapAttributeConfig;
|
||||||
|
import io.kamax.mxisd.config.ldap.LdapConfig;
|
||||||
|
import io.kamax.mxisd.controller.directory.io.UserDirectorySearchResult;
|
||||||
|
import io.kamax.mxisd.directory.IDirectoryProvider;
|
||||||
|
import io.kamax.mxisd.exception.InternalServerError;
|
||||||
|
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;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class LdapDirectoryProvider extends LdapGenericBackend implements IDirectoryProvider {
|
||||||
|
|
||||||
|
private Logger log = LoggerFactory.getLogger(LdapDirectoryProvider.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public LdapDirectoryProvider(LdapConfig cfg, MatrixConfig mxCfg) {
|
||||||
|
super(cfg, mxCfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return getCfg().isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected UserDirectorySearchResult search(String query, List<String> attributes) {
|
||||||
|
UserDirectorySearchResult result = new UserDirectorySearchResult();
|
||||||
|
result.setLimited(false);
|
||||||
|
|
||||||
|
try (LdapConnection conn = getConn()) {
|
||||||
|
bind(conn);
|
||||||
|
|
||||||
|
LdapAttributeConfig atCfg = getCfg().getAttribute();
|
||||||
|
|
||||||
|
attributes = new ArrayList<>(attributes);
|
||||||
|
attributes.add(getUidAtt());
|
||||||
|
String[] attArray = new String[attributes.size()];
|
||||||
|
attributes.toArray(attArray);
|
||||||
|
|
||||||
|
String searchQuery = buildOrQueryWithFilter(getCfg().getDirectory().getFilter(), "*" + query + "*", attArray);
|
||||||
|
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());
|
||||||
|
getAttribute(entry, getUidAtt()).ifPresent(uid -> {
|
||||||
|
log.info("DN {} is a valid match", entry.getDn().getName());
|
||||||
|
try {
|
||||||
|
UserDirectorySearchResult.Result entryResult = new UserDirectorySearchResult.Result();
|
||||||
|
entryResult.setUserId(buildMatrixIdFromUid(uid));
|
||||||
|
getAttribute(entry, atCfg.getName()).ifPresent(entryResult::setDisplayName);
|
||||||
|
result.addResult(entryResult);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
log.warn("Bind was found but type {} is not supported", atCfg.getUid().getType());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (CursorLdapReferralException e) {
|
||||||
|
log.warn("An entry is only available via referral, skipping");
|
||||||
|
} catch (IOException | LdapException | CursorException e) {
|
||||||
|
throw new InternalServerError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDirectorySearchResult searchByDisplayName(String query) {
|
||||||
|
log.info("Performing LDAP directory search on display name using '{}'", query);
|
||||||
|
return search(query, Collections.singletonList(getCfg().getAttribute().getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDirectorySearchResult searchBy3pid(String query) {
|
||||||
|
log.info("Performing LDAP directory search on 3PIDs using '{}'", query);
|
||||||
|
List<String> attributes = new ArrayList<>();
|
||||||
|
getCfg().getAttribute().getThreepid().forEach((k, v) -> attributes.addAll(v));
|
||||||
|
return search(query, attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -20,38 +20,112 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.backend.ldap;
|
package io.kamax.mxisd.backend.ldap;
|
||||||
|
|
||||||
|
import io.kamax.mxisd.config.MatrixConfig;
|
||||||
|
import io.kamax.mxisd.config.ldap.LdapAttributeConfig;
|
||||||
import io.kamax.mxisd.config.ldap.LdapConfig;
|
import io.kamax.mxisd.config.ldap.LdapConfig;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.directory.api.ldap.model.entry.Attribute;
|
||||||
|
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.exception.LdapException;
|
||||||
import org.apache.directory.ldap.client.api.LdapConnection;
|
import org.apache.directory.ldap.client.api.LdapConnection;
|
||||||
import org.apache.directory.ldap.client.api.LdapNetworkConnection;
|
import org.apache.directory.ldap.client.api.LdapNetworkConnection;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
@Component
|
import java.util.Arrays;
|
||||||
public class LdapGenericBackend {
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public abstract class LdapGenericBackend {
|
||||||
|
|
||||||
|
public static final String UID = "uid";
|
||||||
|
public static final String MATRIX_ID = "mxid";
|
||||||
|
|
||||||
private Logger log = LoggerFactory.getLogger(LdapGenericBackend.class);
|
private Logger log = LoggerFactory.getLogger(LdapGenericBackend.class);
|
||||||
|
|
||||||
@Autowired
|
private LdapConfig cfg;
|
||||||
private LdapConfig ldapCfg;
|
private MatrixConfig mxCfg;
|
||||||
|
|
||||||
protected LdapConnection getConn() {
|
public LdapGenericBackend(LdapConfig cfg, MatrixConfig mxCfg) {
|
||||||
return new LdapNetworkConnection(ldapCfg.getConn().getHost(), ldapCfg.getConn().getPort(), ldapCfg.getConn().isTls());
|
this.cfg = cfg;
|
||||||
}
|
this.mxCfg = mxCfg;
|
||||||
|
|
||||||
protected void bind(LdapConnection conn) throws LdapException {
|
|
||||||
if (StringUtils.isBlank(ldapCfg.getConn().getBindDn()) && StringUtils.isBlank(ldapCfg.getConn().getBindPassword())) {
|
|
||||||
conn.anonymousBind();
|
|
||||||
} else {
|
|
||||||
conn.bind(ldapCfg.getConn().getBindDn(), ldapCfg.getConn().getBindPassword());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected LdapConfig getCfg() {
|
protected LdapConfig getCfg() {
|
||||||
return ldapCfg;
|
return cfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getBaseDn() {
|
||||||
|
return cfg.getConn().getBaseDn();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected LdapAttributeConfig getAt() {
|
||||||
|
return cfg.getAttribute();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getUidAtt() {
|
||||||
|
return getAt().getUid().getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected LdapConnection getConn() {
|
||||||
|
return new LdapNetworkConnection(cfg.getConn().getHost(), cfg.getConn().getPort(), cfg.getConn().isTls());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void bind(LdapConnection conn) throws LdapException {
|
||||||
|
if (StringUtils.isBlank(cfg.getConn().getBindDn()) && StringUtils.isBlank(cfg.getConn().getBindPassword())) {
|
||||||
|
conn.anonymousBind();
|
||||||
|
} else {
|
||||||
|
conn.bind(cfg.getConn().getBindDn(), cfg.getConn().getBindPassword());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String buildWithFilter(String base, String filter) {
|
||||||
|
if (StringUtils.isBlank(filter)) {
|
||||||
|
return base;
|
||||||
|
} else {
|
||||||
|
return "(&" + filter + base + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String buildOrQuery(String value, String... attributes) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append("(|");
|
||||||
|
Arrays.stream(attributes).forEach(s -> {
|
||||||
|
builder.append("(");
|
||||||
|
builder.append(s).append("=").append(value).append(")");
|
||||||
|
});
|
||||||
|
builder.append(")");
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String buildOrQueryWithFilter(String filter, String value, String... attributes) {
|
||||||
|
return buildWithFilter(buildOrQuery(value, attributes), filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String buildMatrixIdFromUid(String uid) {
|
||||||
|
String uidType = getCfg().getAttribute().getUid().getType();
|
||||||
|
if (StringUtils.equals(UID, uidType)) {
|
||||||
|
return "@" + uid + ":" + mxCfg.getDomain();
|
||||||
|
} else if (StringUtils.equals(MATRIX_ID, uidType)) {
|
||||||
|
return uid;
|
||||||
|
} 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) {
|
||||||
|
log.info("DN {}: no attribute {}, skipping", entry.getDn(), attName);
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
String value = attribute.get().toString();
|
||||||
|
if (StringUtils.isBlank(value)) {
|
||||||
|
log.info("DN {}: empty attribute {}, skipping", attName);
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.of(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -21,23 +21,21 @@
|
|||||||
package io.kamax.mxisd.backend.ldap;
|
package io.kamax.mxisd.backend.ldap;
|
||||||
|
|
||||||
import io.kamax.mxisd.config.MatrixConfig;
|
import io.kamax.mxisd.config.MatrixConfig;
|
||||||
|
import io.kamax.mxisd.config.ldap.LdapConfig;
|
||||||
import io.kamax.mxisd.exception.InternalServerError;
|
import io.kamax.mxisd.exception.InternalServerError;
|
||||||
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 io.kamax.mxisd.lookup.provider.IThreePidProvider;
|
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
|
||||||
import org.apache.commons.lang.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;
|
||||||
import org.apache.directory.api.ldap.model.entry.Attribute;
|
|
||||||
import org.apache.directory.api.ldap.model.entry.Entry;
|
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.exception.LdapException;
|
||||||
import org.apache.directory.api.ldap.model.message.SearchScope;
|
import org.apache.directory.api.ldap.model.message.SearchScope;
|
||||||
import org.apache.directory.ldap.client.api.LdapConnection;
|
import org.apache.directory.ldap.client.api.LdapConnection;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -48,23 +46,17 @@ import java.util.Optional;
|
|||||||
@Component
|
@Component
|
||||||
public class LdapThreePidProvider extends LdapGenericBackend implements IThreePidProvider {
|
public class LdapThreePidProvider extends LdapGenericBackend implements IThreePidProvider {
|
||||||
|
|
||||||
public static final String UID = "uid";
|
|
||||||
public static final String MATRIX_ID = "mxid";
|
|
||||||
|
|
||||||
private Logger log = LoggerFactory.getLogger(LdapThreePidProvider.class);
|
private Logger log = LoggerFactory.getLogger(LdapThreePidProvider.class);
|
||||||
|
|
||||||
@Autowired
|
public LdapThreePidProvider(LdapConfig cfg, MatrixConfig mxCfg) {
|
||||||
private MatrixConfig mxCfg;
|
super(cfg, mxCfg);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return getCfg().isEnabled();
|
return getCfg().isEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getUidAttribute() {
|
|
||||||
return getCfg().getAttribute().getUid().getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isLocal() {
|
public boolean isLocal() {
|
||||||
return true;
|
return true;
|
||||||
@@ -76,46 +68,22 @@ public class LdapThreePidProvider extends LdapGenericBackend implements IThreePi
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Optional<String> lookup(LdapConnection conn, String medium, String value) {
|
private Optional<String> lookup(LdapConnection conn, String medium, String value) {
|
||||||
String uidAttribute = getUidAttribute();
|
|
||||||
|
|
||||||
Optional<String> queryOpt = getCfg().getIdentity().getQuery(medium);
|
Optional<String> queryOpt = getCfg().getIdentity().getQuery(medium);
|
||||||
if (!queryOpt.isPresent()) {
|
if (!queryOpt.isPresent()) {
|
||||||
log.warn("{} is not a configured 3PID type for LDAP lookup", medium);
|
log.warn("{} is not a configured 3PID type for LDAP lookup", medium);
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
String searchQuery = queryOpt.get().replaceAll("%3pid", value);
|
String searchQuery = queryOpt.get().replaceAll(getCfg().getIdentity().getToken(), value);
|
||||||
try (EntryCursor cursor = conn.search(getCfg().getConn().getBaseDn(), searchQuery, SearchScope.SUBTREE, uidAttribute)) {
|
try (EntryCursor cursor = conn.search(getBaseDn(), searchQuery, SearchScope.SUBTREE, getUidAtt())) {
|
||||||
while (cursor.next()) {
|
while (cursor.next()) {
|
||||||
Entry entry = cursor.get();
|
Entry entry = cursor.get();
|
||||||
log.info("Found possible match, DN: {}", entry.getDn().getName());
|
log.info("Found possible match, DN: {}", entry.getDn().getName());
|
||||||
|
|
||||||
Attribute attribute = entry.get(uidAttribute);
|
getAttribute(entry, getUidAtt()).map(uid -> {
|
||||||
if (attribute == null) {
|
|
||||||
log.info("DN {}: no attribute {}, skpping", entry.getDn(), getCfg().getAttribute());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String data = attribute.get().toString();
|
|
||||||
if (data.length() < 1) {
|
|
||||||
log.info("DN {}: empty attribute {}, skipping", getCfg().getAttribute());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder matrixId = new StringBuilder();
|
|
||||||
// TODO Should we turn this block into a map of functions?
|
|
||||||
String uidType = getCfg().getAttribute().getUid().getType();
|
|
||||||
if (StringUtils.equals(UID, uidType)) {
|
|
||||||
matrixId.append("@").append(data).append(":").append(mxCfg.getDomain());
|
|
||||||
} else if (StringUtils.equals(MATRIX_ID, uidType)) {
|
|
||||||
matrixId.append(data);
|
|
||||||
} else {
|
|
||||||
log.warn("Bind was found but type {} is not supported", uidType);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("DN {} is a valid match", entry.getDn().getName());
|
log.info("DN {} is a valid match", entry.getDn().getName());
|
||||||
return Optional.of(matrixId.toString());
|
return buildMatrixIdFromUid(uid);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (CursorLdapReferralException e) {
|
} catch (CursorLdapReferralException e) {
|
||||||
log.warn("3PID {} is only available via referral, skipping", value);
|
log.warn("3PID {} is only available via referral, skipping", value);
|
||||||
@@ -128,15 +96,11 @@ public class LdapThreePidProvider extends LdapGenericBackend implements IThreePi
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<SingleLookupReply> find(SingleLookupRequest request) {
|
public Optional<SingleLookupReply> find(SingleLookupRequest request) {
|
||||||
log.info("Performing LDAP lookup ${request.getThreePid()} of type ${request.getType()}");
|
log.info("Performing LDAP lookup {} of type {}", request.getThreePid(), request.getType());
|
||||||
|
|
||||||
try (LdapConnection conn = getConn()) {
|
try (LdapConnection conn = getConn()) {
|
||||||
bind(conn);
|
bind(conn);
|
||||||
|
lookup(conn, request.getType(), request.getThreePid()).map(id -> new SingleLookupReply(request, id));
|
||||||
Optional<String> mxid = lookup(conn, request.getType(), request.getThreePid());
|
|
||||||
if (mxid.isPresent()) {
|
|
||||||
return Optional.of(new SingleLookupReply(request, mxid.get()));
|
|
||||||
}
|
|
||||||
} catch (LdapException | IOException e) {
|
} catch (LdapException | IOException e) {
|
||||||
throw new InternalServerError(e);
|
throw new InternalServerError(e);
|
||||||
}
|
}
|
||||||
@@ -155,11 +119,10 @@ public class LdapThreePidProvider extends LdapGenericBackend implements IThreePi
|
|||||||
|
|
||||||
for (ThreePidMapping mapping : mappings) {
|
for (ThreePidMapping mapping : mappings) {
|
||||||
try {
|
try {
|
||||||
Optional<String> mxid = lookup(conn, mapping.getMedium(), mapping.getValue());
|
lookup(conn, mapping.getMedium(), mapping.getValue()).ifPresent(id -> {
|
||||||
if (mxid.isPresent()) {
|
mapping.setMxid(id);
|
||||||
mapping.setMxid(mxid.get());
|
|
||||||
mappingsFound.add(mapping);
|
mappingsFound.add(mapping);
|
||||||
}
|
});
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
log.warn("{} is not a supported 3PID type for LDAP lookup", mapping.getMedium());
|
log.warn("{} is not a supported 3PID type for LDAP lookup", mapping.getMedium());
|
||||||
}
|
}
|
||||||
|
@@ -23,12 +23,17 @@ package io.kamax.mxisd.config.ldap;
|
|||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConfigurationProperties(prefix = "ldap.attribute")
|
@ConfigurationProperties(prefix = "ldap.attribute")
|
||||||
public class LdapAttributeConfig {
|
public class LdapAttributeConfig {
|
||||||
|
|
||||||
private LdapAttributeUidConfig uid;
|
private LdapAttributeUidConfig uid;
|
||||||
private String name;
|
private String name;
|
||||||
|
private Map<String, List<String>> threepid = new HashMap<>();
|
||||||
|
|
||||||
public LdapAttributeUidConfig getUid() {
|
public LdapAttributeUidConfig getUid() {
|
||||||
return uid;
|
return uid;
|
||||||
@@ -46,4 +51,12 @@ public class LdapAttributeConfig {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<String, List<String>> getThreepid() {
|
||||||
|
return threepid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setThreepid(Map<String, List<String>> threepid) {
|
||||||
|
this.threepid = threepid;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,9 @@
|
|||||||
package io.kamax.mxisd.config.ldap;
|
package io.kamax.mxisd.config.ldap;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import io.kamax.mxisd.backend.ldap.LdapThreePidProvider;
|
import io.kamax.matrix.ThreePidMedium;
|
||||||
|
import io.kamax.mxisd.backend.ldap.LdapGenericBackend;
|
||||||
|
import io.kamax.mxisd.exception.ConfigurationException;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -35,16 +37,31 @@ import javax.annotation.PostConstruct;
|
|||||||
@ConfigurationProperties(prefix = "ldap")
|
@ConfigurationProperties(prefix = "ldap")
|
||||||
public class LdapConfig {
|
public class LdapConfig {
|
||||||
|
|
||||||
|
private Logger log = LoggerFactory.getLogger(LdapConfig.class);
|
||||||
private static Gson gson = new Gson();
|
private static Gson gson = new Gson();
|
||||||
|
|
||||||
private Logger log = LoggerFactory.getLogger(LdapConfig.class);
|
|
||||||
|
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
|
private String filter;
|
||||||
|
|
||||||
|
public static class Directory {
|
||||||
|
|
||||||
|
private String filter;
|
||||||
|
|
||||||
|
public String getFilter() {
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFilter(String filter) {
|
||||||
|
this.filter = filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private LdapConnectionConfig conn;
|
private LdapConnectionConfig conn;
|
||||||
private LdapAttributeConfig attribute;
|
private LdapAttributeConfig attribute;
|
||||||
private LdapAuthConfig auth;
|
private LdapAuthConfig auth;
|
||||||
|
private Directory directory;
|
||||||
private LdapIdentityConfig identity;
|
private LdapIdentityConfig identity;
|
||||||
|
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
@@ -55,6 +72,14 @@ public class LdapConfig {
|
|||||||
this.enabled = enabled;
|
this.enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getFilter() {
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFilter(String filter) {
|
||||||
|
this.filter = filter;
|
||||||
|
}
|
||||||
|
|
||||||
public LdapConnectionConfig getConn() {
|
public LdapConnectionConfig getConn() {
|
||||||
return conn;
|
return conn;
|
||||||
}
|
}
|
||||||
@@ -79,6 +104,14 @@ public class LdapConfig {
|
|||||||
this.auth = auth;
|
this.auth = auth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Directory getDirectory() {
|
||||||
|
return directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDirectory(Directory directory) {
|
||||||
|
this.directory = directory;
|
||||||
|
}
|
||||||
|
|
||||||
public LdapIdentityConfig getIdentity() {
|
public LdapIdentityConfig getIdentity() {
|
||||||
return identity;
|
return identity;
|
||||||
}
|
}
|
||||||
@@ -100,7 +133,7 @@ public class LdapConfig {
|
|||||||
throw new IllegalStateException("LDAP Host must be configured!");
|
throw new IllegalStateException("LDAP Host must be configured!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (1 > conn.getPort() || 65535 < conn.getPort()) {
|
if (conn.getPort() < 1 || conn.getPort() > 65535) {
|
||||||
throw new IllegalStateException("LDAP port is not valid");
|
throw new IllegalStateException("LDAP port is not valid");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,10 +147,29 @@ public class LdapConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String uidType = attribute.getUid().getType();
|
String uidType = attribute.getUid().getType();
|
||||||
if (!StringUtils.equals(LdapThreePidProvider.UID, uidType) && !StringUtils.equals(LdapThreePidProvider.MATRIX_ID, uidType)) {
|
if (!StringUtils.equals(LdapGenericBackend.UID, uidType) && !StringUtils.equals(LdapGenericBackend.MATRIX_ID, uidType)) {
|
||||||
throw new IllegalArgumentException("Unsupported LDAP UID type: " + uidType);
|
throw new IllegalArgumentException("Unsupported LDAP UID type: " + uidType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(identity.getToken())) {
|
||||||
|
throw new ConfigurationException("ldap.identity.token");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build queries
|
||||||
|
attribute.getThreepid().forEach((k, v) -> {
|
||||||
|
if (StringUtils.isBlank(identity.getMedium().get(k))) {
|
||||||
|
if (ThreePidMedium.PhoneNumber.is(k)) {
|
||||||
|
identity.getMedium().put(k, LdapGenericBackend.buildOrQuery("+" + getIdentity().getToken()));
|
||||||
|
} else {
|
||||||
|
identity.getMedium().put(k, LdapGenericBackend.buildOrQuery(getIdentity().getToken()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
getAuth().setFilter(StringUtils.defaultIfBlank(getAuth().getFilter(), getFilter()));
|
||||||
|
getDirectory().setFilter(StringUtils.defaultIfBlank(getDirectory().getFilter(), getFilter()));
|
||||||
|
getIdentity().setFilter(StringUtils.defaultIfBlank(getIdentity().getFilter(), getFilter()));
|
||||||
|
|
||||||
log.info("Host: {}", conn.getHost());
|
log.info("Host: {}", conn.getHost());
|
||||||
log.info("Port: {}", conn.getPort());
|
log.info("Port: {}", conn.getPort());
|
||||||
log.info("Bind DN: {}", conn.getBindDn());
|
log.info("Bind DN: {}", conn.getBindDn());
|
||||||
@@ -125,6 +177,7 @@ public class LdapConfig {
|
|||||||
|
|
||||||
log.info("Attribute: {}", gson.toJson(attribute));
|
log.info("Attribute: {}", gson.toJson(attribute));
|
||||||
log.info("Auth: {}", gson.toJson(auth));
|
log.info("Auth: {}", gson.toJson(auth));
|
||||||
|
log.info("Directory: {}", gson.toJson(directory));
|
||||||
log.info("Identity: {}", gson.toJson(identity));
|
log.info("Identity: {}", gson.toJson(identity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -31,8 +31,26 @@ import java.util.Optional;
|
|||||||
@ConfigurationProperties(prefix = "ldap.identity")
|
@ConfigurationProperties(prefix = "ldap.identity")
|
||||||
public class LdapIdentityConfig {
|
public class LdapIdentityConfig {
|
||||||
|
|
||||||
|
private String filter;
|
||||||
|
private String token = "%3pid";
|
||||||
private Map<String, String> medium = new HashMap<>();
|
private Map<String, String> medium = new HashMap<>();
|
||||||
|
|
||||||
|
public String getFilter() {
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFilter(String filter) {
|
||||||
|
this.filter = filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getToken() {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setToken(String token) {
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
|
||||||
public Map<String, String> getMedium() {
|
public Map<String, String> getMedium() {
|
||||||
return medium;
|
return medium;
|
||||||
}
|
}
|
||||||
|
@@ -45,65 +45,66 @@ public class DefaultExceptionHandler {
|
|||||||
|
|
||||||
private static Gson gson = new Gson();
|
private static Gson gson = new Gson();
|
||||||
|
|
||||||
static String handle(String erroCode, String error) {
|
private String handle(HttpServletRequest req, String erroCode, String error) {
|
||||||
JsonObject obj = new JsonObject();
|
JsonObject obj = new JsonObject();
|
||||||
obj.addProperty("errcode", erroCode);
|
obj.addProperty("errcode", erroCode);
|
||||||
obj.addProperty("error", error);
|
obj.addProperty("error", error);
|
||||||
obj.addProperty("success", false);
|
obj.addProperty("success", false);
|
||||||
|
log.info("Request {} {} - Error {}: {}", req.getMethod(), req.getRequestURL(), erroCode, error);
|
||||||
return gson.toJson(obj);
|
return gson.toJson(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExceptionHandler(InternalServerError.class)
|
@ExceptionHandler(InternalServerError.class)
|
||||||
public String handle(InternalServerError e, HttpServletResponse response) {
|
public String handle(HttpServletRequest req, InternalServerError e, HttpServletResponse response) {
|
||||||
if (StringUtils.isNotBlank(e.getInternalReason())) {
|
if (StringUtils.isNotBlank(e.getInternalReason())) {
|
||||||
log.error("Reference #{} - {}", e.getReference(), e.getInternalReason());
|
log.error("Reference #{} - {}", e.getReference(), e.getInternalReason());
|
||||||
} else {
|
} else {
|
||||||
log.error("Reference #{}", e);
|
log.error("Reference #{}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return handleGeneric(e, response);
|
return handleGeneric(req, e, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExceptionHandler(MatrixException.class)
|
@ExceptionHandler(MatrixException.class)
|
||||||
public String handleGeneric(MatrixException e, HttpServletResponse response) {
|
public String handleGeneric(HttpServletRequest req, MatrixException e, HttpServletResponse response) {
|
||||||
response.setStatus(e.getStatus());
|
response.setStatus(e.getStatus());
|
||||||
return handle(e.getErrorCode(), e.getError());
|
return handle(req, e.getErrorCode(), e.getError());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||||
@ExceptionHandler(MissingServletRequestParameterException.class)
|
@ExceptionHandler(MissingServletRequestParameterException.class)
|
||||||
public String handle(MissingServletRequestParameterException e) {
|
public String handle(HttpServletRequest req, MissingServletRequestParameterException e) {
|
||||||
return handle("M_INCOMPLETE_REQUEST", e.getMessage());
|
return handle(req, "M_INCOMPLETE_REQUEST", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||||
@ExceptionHandler(InvalidResponseJsonException.class)
|
@ExceptionHandler(InvalidResponseJsonException.class)
|
||||||
public String handle(InvalidResponseJsonException e) {
|
public String handle(HttpServletRequest req, InvalidResponseJsonException e) {
|
||||||
return handle("M_INVALID_JSON", e.getMessage());
|
return handle(req, "M_INVALID_JSON", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||||
@ExceptionHandler(JsonSyntaxException.class)
|
@ExceptionHandler(JsonSyntaxException.class)
|
||||||
public String handle(JsonSyntaxException e) {
|
public String handle(HttpServletRequest req, JsonSyntaxException e) {
|
||||||
return handle("M_INVALID_JSON", e.getMessage());
|
return handle(req, "M_INVALID_JSON", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||||
@ExceptionHandler(JsonMemberNotFoundException.class)
|
@ExceptionHandler(JsonMemberNotFoundException.class)
|
||||||
public String handle(JsonMemberNotFoundException e) {
|
public String handle(HttpServletRequest req, JsonMemberNotFoundException e) {
|
||||||
return handle("M_JSON_MISSING_KEYS", e.getMessage());
|
return handle(req, "M_JSON_MISSING_KEYS", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||||
@ExceptionHandler(MappingAlreadyExistsException.class)
|
@ExceptionHandler(MappingAlreadyExistsException.class)
|
||||||
public String handle(MappingAlreadyExistsException e) {
|
public String handle(HttpServletRequest req, MappingAlreadyExistsException e) {
|
||||||
return handle("M_ALREADY_EXISTS", e.getMessage());
|
return handle(req, "M_ALREADY_EXISTS", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||||
@ExceptionHandler(BadRequestException.class)
|
@ExceptionHandler(BadRequestException.class)
|
||||||
public String handle(BadRequestException e) {
|
public String handle(HttpServletRequest req, BadRequestException e) {
|
||||||
return handle("M_BAD_REQUEST", e.getMessage());
|
return handle(req, "M_BAD_REQUEST", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||||
@@ -111,6 +112,7 @@ public class DefaultExceptionHandler {
|
|||||||
public String handle(HttpServletRequest req, RuntimeException e) {
|
public String handle(HttpServletRequest req, RuntimeException e) {
|
||||||
log.error("Unknown error when handling {}", req.getRequestURL(), e);
|
log.error("Unknown error when handling {}", req.getRequestURL(), e);
|
||||||
return handle(
|
return handle(
|
||||||
|
req,
|
||||||
"M_UNKNOWN",
|
"M_UNKNOWN",
|
||||||
StringUtils.defaultIfBlank(
|
StringUtils.defaultIfBlank(
|
||||||
e.getMessage(),
|
e.getMessage(),
|
||||||
|
@@ -44,6 +44,7 @@ rest:
|
|||||||
|
|
||||||
ldap:
|
ldap:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
filter: ''
|
||||||
connection:
|
connection:
|
||||||
tls: false
|
tls: false
|
||||||
port: 389
|
port: 389
|
||||||
@@ -52,10 +53,27 @@ ldap:
|
|||||||
type: 'uid'
|
type: 'uid'
|
||||||
value: 'userPrincipalName'
|
value: 'userPrincipalName'
|
||||||
name: 'displayName'
|
name: 'displayName'
|
||||||
|
threepid:
|
||||||
|
email:
|
||||||
|
- 'mailPrimaryAddress'
|
||||||
|
- 'mail'
|
||||||
|
- 'otherMailbox'
|
||||||
|
msisdn:
|
||||||
|
- 'telephoneNumber'
|
||||||
|
- 'mobile'
|
||||||
|
- 'homePhone'
|
||||||
|
- 'otherTelephone'
|
||||||
|
- 'otherMobile'
|
||||||
|
- 'otherHomePhone'
|
||||||
|
auth:
|
||||||
|
filter: ''
|
||||||
|
directory:
|
||||||
|
filter: ''
|
||||||
identity:
|
identity:
|
||||||
|
filter: ''
|
||||||
medium:
|
medium:
|
||||||
email: "(|(mailPrimaryAddress=%3pid)(mail=%3pid)(otherMailbox=%3pid))"
|
email: ''
|
||||||
msisdn: "(|(telephoneNumber=+%3pid)(mobile=+%3pid)(homePhone=+%3pid)(otherTelephone=+%3pid)(otherMobile=+%3pid)(otherHomePhone=+%3pid))"
|
msisdn: ''
|
||||||
|
|
||||||
firebase:
|
firebase:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
Reference in New Issue
Block a user