diff --git a/application.example.yaml b/application.example.yaml index a52d1f4..05d3891 100644 --- a/application.example.yaml +++ b/application.example.yaml @@ -172,12 +172,14 @@ key.path: '/path/to/sign.key' #ldap.connection.port: 389 -# Bind DN to use when performing lookups +# Bind DN for the connection. +# +# If Bind DN and password are empty, anonymous authentication is performed # #ldap.connection.bindDn: 'CN=Matrix Identity Server,CN=Users,DC=example,DC=org' -# Bind password to use +# Bind password for the connection. # #ldap.connection.bindPassword: 'password' @@ -256,6 +258,9 @@ key.path: '/path/to/sign.key' #sql.enabled: true #sql.type: 'sqlite' #sql.connection: '/var/lib/matrix-synapse/homeserver.db' + + +# See ldap.attribute.uid.type for more info on possible values #sql.identity.type: 'mxid' #sql.identity.query: 'SELECT user_id AS uid FROM user_threepids WHERE medium = ? AND address = ?' #sql.identity.medium.email: "SELECT user_id AS uid FROM user_threepids WHERE medium = ? AND address = ?" @@ -383,6 +388,7 @@ storage.provider.sqlite.database: '/path/to/mxisd.db' #dns.overwrite.homeserver.type: 'raw' -# The value to use, depending on the type +# The value to use, depending on the type. +# Protocol will always be HTTPS # #dns.overwrite.homeserver.value: 'localhost:8448' diff --git a/src/main/groovy/io/kamax/mxisd/auth/provider/LdapAuthProvider.java b/src/main/groovy/io/kamax/mxisd/auth/provider/LdapAuthProvider.java index 6edeaa6..63f19d0 100644 --- a/src/main/groovy/io/kamax/mxisd/auth/provider/LdapAuthProvider.java +++ b/src/main/groovy/io/kamax/mxisd/auth/provider/LdapAuthProvider.java @@ -22,7 +22,7 @@ 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.backend.LdapBackend; import io.kamax.mxisd.lookup.provider.LdapProvider; import org.apache.commons.lang.StringUtils; import org.apache.directory.api.ldap.model.cursor.CursorException; @@ -33,37 +33,24 @@ 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 { +public class LdapAuthProvider extends LdapBackend 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(); + return getCfg().getAttribute().getUid().getValue(); } @Override public boolean isEnabled() { - return ldapCfg.isEnabled(); + return getCfg().isEnabled(); } @Override @@ -74,11 +61,11 @@ public class LdapAuthProvider implements AuthenticatorProvider { try { bind(conn); - String uidType = ldapCfg.getAttribute().getUid().getType(); + String uidType = getCfg().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()); + String userFilter = "(" + getCfg().getAttribute().getUid().getValue() + "=" + userFilterValue + ")"; + EntryCursor cursor = conn.search(getCfg().getConn().getBaseDn(), userFilter, SearchScope.SUBTREE, getUidAttribute(), getCfg().getAttribute().getName()); try { while (cursor.next()) { Entry entry = cursor.get(); @@ -105,7 +92,7 @@ public class LdapAuthProvider implements AuthenticatorProvider { return new UserAuthResult().failure(); } - Attribute nameAttribute = entry.get(ldapCfg.getAttribute().getName()); + Attribute nameAttribute = entry.get(getCfg().getAttribute().getName()); String name = nameAttribute != null ? nameAttribute.get().toString() : null; log.info("Authentication successful for {}", entry.getDn().getName()); diff --git a/src/main/groovy/io/kamax/mxisd/backend/LdapBackend.java b/src/main/groovy/io/kamax/mxisd/backend/LdapBackend.java new file mode 100644 index 0000000..9a735ff --- /dev/null +++ b/src/main/groovy/io/kamax/mxisd/backend/LdapBackend.java @@ -0,0 +1,57 @@ +/* + * mxisd - Matrix Identity Server Daemon + * Copyright (C) 2017 Maxime Dor + * + * https://max.kamax.io/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package io.kamax.mxisd.backend; + +import io.kamax.mxisd.config.ldap.LdapConfig; +import org.apache.commons.lang.StringUtils; +import org.apache.directory.api.ldap.model.exception.LdapException; +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; + +@Component +public class LdapBackend { + + private Logger log = LoggerFactory.getLogger(LdapBackend.class); + + @Autowired + private LdapConfig ldapCfg; + + protected LdapConnection getConn() { + return new LdapNetworkConnection(ldapCfg.getConn().getHost(), ldapCfg.getConn().getPort(), ldapCfg.getConn().isTls()); + } + + 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() { + return ldapCfg; + } + +} diff --git a/src/main/groovy/io/kamax/mxisd/lookup/provider/LdapProvider.groovy b/src/main/groovy/io/kamax/mxisd/lookup/provider/LdapProvider.groovy index d8bb349..f39c7e6 100644 --- a/src/main/groovy/io/kamax/mxisd/lookup/provider/LdapProvider.groovy +++ b/src/main/groovy/io/kamax/mxisd/lookup/provider/LdapProvider.groovy @@ -20,8 +20,8 @@ package io.kamax.mxisd.lookup.provider +import io.kamax.mxisd.backend.LdapBackend import io.kamax.mxisd.config.MatrixConfig -import io.kamax.mxisd.config.ldap.LdapConfig import io.kamax.mxisd.lookup.SingleLookupReply import io.kamax.mxisd.lookup.SingleLookupRequest import io.kamax.mxisd.lookup.ThreePidMapping @@ -32,14 +32,13 @@ 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.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 @Component -class LdapProvider implements IThreePidProvider { +class LdapProvider extends LdapBackend implements IThreePidProvider { public static final String UID = "uid" public static final String MATRIX_ID = "mxid" @@ -49,24 +48,13 @@ class LdapProvider implements IThreePidProvider { @Autowired private MatrixConfig mxCfg - @Autowired - private LdapConfig ldapCfg - @Override boolean isEnabled() { - return ldapCfg.isEnabled() - } - - private LdapConnection getConn() { - return new LdapNetworkConnection(ldapCfg.getConn().getHost(), ldapCfg.getConn().getPort(), ldapCfg.getConn().isTls()) - } - - private void bind(LdapConnection conn) { - conn.bind(ldapCfg.getConn().getBindDn(), ldapCfg.getConn().getBindPassword()) + return getCfg().isEnabled() } private String getUidAttribute() { - return ldapCfg.getAttribute().getUid().getValue(); + return getCfg().getAttribute().getUid().getValue(); } @Override @@ -82,14 +70,14 @@ class LdapProvider implements IThreePidProvider { Optional lookup(LdapConnection conn, String medium, String value) { String uidAttribute = getUidAttribute() - Optional queryOpt = ldapCfg.getIdentity().getQuery(medium) + Optional queryOpt = getCfg().getIdentity().getQuery(medium) if (!queryOpt.isPresent()) { log.warn("{} is not a configured 3PID type for LDAP lookup", medium) return Optional.empty() } String searchQuery = queryOpt.get().replaceAll("%3pid", value) - EntryCursor cursor = conn.search(ldapCfg.getConn().getBaseDn(), searchQuery, SearchScope.SUBTREE, uidAttribute) + EntryCursor cursor = conn.search(getCfg().getConn().getBaseDn(), searchQuery, SearchScope.SUBTREE, uidAttribute) try { while (cursor.next()) { Entry entry = cursor.get() @@ -97,19 +85,19 @@ class LdapProvider implements IThreePidProvider { Attribute attribute = entry.get(uidAttribute) if (attribute == null) { - log.info("DN {}: no attribute {}, skpping", entry.getDn(), ldapCfg.getAttribute()) + 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", ldapCfg.getAttribute()) + 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 = ldapCfg.getAttribute().getUid().getType() + 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)) {