Add LDAP Auth support with synapse REST Auth module

This commit is contained in:
Maxime Dor
2017-09-05 21:31:36 +02:00
parent 85236793e1
commit 4b0d549dd6
6 changed files with 136 additions and 17 deletions

View File

@@ -158,6 +158,8 @@ ldap:
# If this value is not set, login check will be performed for all entities within the LDAP
#
# Example: (memberOf=CN=Matrix Users,CN=Users,DC=example,DC=org)
#
# /!\ Currently NOT supported due to a possible bug in LDAP library /!\
filter: ''
# Configuration section relating to identity lookups

View File

@@ -47,7 +47,7 @@ dependencies {
compile 'org.springframework.boot:spring-boot-starter-web:1.5.3.RELEASE'
// Matrix Java SDK
compile 'io.kamax:matrix-java-sdk:0.0.1'
compile 'io.kamax:matrix-java-sdk:0.0.2'
// ed25519 handling
compile 'net.i2p.crypto:eddsa:0.1.0'

View File

@@ -14,10 +14,12 @@ public class UserAuthResult {
return this;
}
public void success(String mxid, String displayName) {
public UserAuthResult success(String mxid, String displayName) {
setSuccess(true);
setMxid(mxid);
setDisplayName(displayName);
return this;
}
public boolean isSuccess() {

View File

@@ -0,0 +1,115 @@
package io.kamax.mxisd.auth.provider;
import io.kamax.matrix.MatrixID;
import io.kamax.mxisd.auth.UserAuthResult;
import io.kamax.mxisd.config.ldap.LdapConfig;
import io.kamax.mxisd.lookup.provider.LdapProvider;
import org.apache.commons.lang.StringUtils;
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.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.message.SearchScope;
import org.apache.directory.ldap.client.api.LdapConnection;
import org.apache.directory.ldap.client.api.LdapNetworkConnection;
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 LdapAuthProvider implements AuthenticatorProvider {
private Logger log = LoggerFactory.getLogger(LdapAuthProvider.class);
@Autowired
private LdapConfig ldapCfg;
private LdapConnection getConn() {
return new LdapNetworkConnection(ldapCfg.getConn().getHost(), ldapCfg.getConn().getPort(), ldapCfg.getConn().isTls());
}
private void bind(LdapConnection conn) throws LdapException {
conn.bind(ldapCfg.getConn().getBindDn(), ldapCfg.getConn().getBindPassword());
}
private String getUidAttribute() {
return ldapCfg.getAttribute().getUid().getValue();
}
@Override
public boolean isEnabled() {
return ldapCfg.isEnabled();
}
@Override
public UserAuthResult authenticate(String id, String password) {
log.info("Performing auth for {}", id);
LdapConnection conn = getConn();
try {
bind(conn);
String uidType = ldapCfg.getAttribute().getUid().getType();
MatrixID mxIdExt = new MatrixID(id);
String userFilterValue = StringUtils.equals(LdapProvider.UID, uidType) ? mxIdExt.getLocalPart() : mxIdExt.getId();
String userFilter = "(" + ldapCfg.getAttribute().getUid().getValue() + "=" + userFilterValue + ")";
EntryCursor cursor = conn.search(ldapCfg.getConn().getBaseDn(), userFilter, SearchScope.SUBTREE, getUidAttribute(), ldapCfg.getAttribute().getName());
try {
while (cursor.next()) {
Entry entry = cursor.get();
String dn = entry.getDn().getName();
log.info("Checking possible match, DN: {}", dn);
Attribute attribute = entry.get(getUidAttribute());
if (attribute == null) {
log.info("DN {}: no attribute {}, skpping", dn, getUidAttribute());
continue;
}
String data = attribute.get().toString();
if (data.length() < 1) {
log.info("DN {}: empty attribute {}, skipping", getUidAttribute());
continue;
}
log.info("Attempting authentication on LDAP for {}", dn);
try {
conn.bind(entry.getDn(), password);
} catch (LdapException e) {
log.info("Unable to bind using {} because {}", entry.getDn().getName(), e.getMessage());
return new UserAuthResult().failure();
}
Attribute nameAttribute = entry.get(ldapCfg.getAttribute().getName());
String name = nameAttribute != null ? nameAttribute.get().toString() : null;
log.info("Authentication successful for {}", entry.getDn().getName());
log.info("DN {} is a valid match", dn);
return new UserAuthResult().success(mxIdExt.getId(), name);
}
} catch (CursorLdapReferralException e) {
log.warn("Entity for {} is only available via referral, skipping", mxIdExt);
} finally {
cursor.close();
}
log.info("No match were found for {}", id);
return new UserAuthResult().failure();
} catch (LdapException | IOException | CursorException e) {
throw new RuntimeException(e);
} finally {
try {
conn.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}

View File

@@ -1,8 +1,12 @@
package io.kamax.mxisd.config.ldap;
import io.kamax.mxisd.lookup.provider.LdapProvider;
import org.apache.commons.lang.StringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
@Configuration
@ConfigurationProperties(prefix = "ldap.attribute.uid")
public class LdapAttributeUidConfig {
@@ -26,4 +30,11 @@ public class LdapAttributeUidConfig {
this.value = value;
}
@PostConstruct
public void postConstruct() {
if (!StringUtils.equals(LdapProvider.UID, getType()) && !StringUtils.equals(LdapProvider.MATRIX_ID, getType())) {
throw new IllegalArgumentException("Unsupported LDAP UID type: " + getType());
}
}
}

View File

@@ -20,8 +20,6 @@
package io.kamax.mxisd.lookup.provider
import io.kamax.mxisd.auth.UserAuthResult
import io.kamax.mxisd.auth.provider.AuthenticatorProvider
import io.kamax.mxisd.config.ServerConfig
import io.kamax.mxisd.config.ldap.LdapConfig
import io.kamax.mxisd.lookup.SingleLookupRequest
@@ -40,7 +38,7 @@ import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
@Component
class LdapProvider implements IThreePidProvider, AuthenticatorProvider {
class LdapProvider implements IThreePidProvider {
public static final String UID = "uid"
public static final String MATRIX_ID = "mxid"
@@ -66,17 +64,8 @@ class LdapProvider implements IThreePidProvider, AuthenticatorProvider {
conn.bind(ldapCfg.getConn().getBindDn(), ldapCfg.getConn().getBindPassword())
}
@Override
UserAuthResult authenticate(String id, String password) {
LdapConnection conn = getConn()
try {
bind(conn)
// TODO finish this
return new UserAuthResult().failure()
} finally {
conn.close()
}
private String getUidAttribute() {
return ldapCfg.getAttribute().getUid().getValue();
}
@Override
@@ -90,7 +79,7 @@ class LdapProvider implements IThreePidProvider, AuthenticatorProvider {
}
Optional<String> lookup(LdapConnection conn, String medium, String value) {
String uidAttribute = ldapCfg.getAttribute().getUid().getValue()
String uidAttribute = getUidAttribute()
Optional<String> queryOpt = ldapCfg.getIdentity().getQuery(medium)
if (!queryOpt.isPresent()) {