Refactor backend packages

This commit is contained in:
Maxime Dor
2017-09-16 22:56:53 +02:00
parent 4eb8c95c3a
commit 23f717579e
9 changed files with 25 additions and 22 deletions

View File

@@ -0,0 +1,121 @@
/*
* 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.matrix.MatrixID;
import io.kamax.mxisd.auth.UserAuthResult;
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class LdapAuthProvider extends LdapGenericBackend implements AuthenticatorProvider {
private Logger log = LoggerFactory.getLogger(LdapAuthProvider.class);
private String getUidAttribute() {
return getCfg().getAttribute().getUid().getValue();
}
@Override
public boolean isEnabled() {
return getCfg().isEnabled();
}
@Override
public UserAuthResult authenticate(String id, String password) {
log.info("Performing auth for {}", id);
LdapConnection conn = getConn();
try {
bind(conn);
String uidType = getCfg().getAttribute().getUid().getType();
MatrixID mxIdExt = new MatrixID(id);
String userFilterValue = StringUtils.equals(LdapThreePidProvider.UID, uidType) ? mxIdExt.getLocalPart() : mxIdExt.getId();
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();
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(getCfg().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

@@ -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 <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.backend.ldap;
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 LdapGenericBackend {
private Logger log = LoggerFactory.getLogger(LdapGenericBackend.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;
}
}

View File

@@ -0,0 +1,169 @@
/*
* 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.lookup.SingleLookupReply
import io.kamax.mxisd.lookup.SingleLookupRequest
import io.kamax.mxisd.lookup.ThreePidMapping
import io.kamax.mxisd.lookup.provider.IThreePidProvider
import org.apache.commons.lang.StringUtils
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.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
@Component
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)
@Autowired
private MatrixConfig mxCfg
@Override
boolean isEnabled() {
return getCfg().isEnabled()
}
private String getUidAttribute() {
return getCfg().getAttribute().getUid().getValue();
}
@Override
boolean isLocal() {
return true
}
@Override
int getPriority() {
return 20
}
Optional<String> lookup(LdapConnection conn, String medium, String value) {
String uidAttribute = getUidAttribute()
Optional<String> 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(getCfg().getConn().getBaseDn(), searchQuery, SearchScope.SUBTREE, uidAttribute)
try {
while (cursor.next()) {
Entry entry = cursor.get()
log.info("Found possible match, DN: {}", entry.getDn().getName())
Attribute attribute = entry.get(uidAttribute)
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())
return Optional.of(matrixId.toString())
}
} catch (CursorLdapReferralException e) {
log.warn("3PID {} is only available via referral, skipping", value)
} finally {
cursor.close()
}
return Optional.empty()
}
@Override
Optional<SingleLookupReply> find(SingleLookupRequest request) {
log.info("Performing LDAP lookup ${request.getThreePid()} of type ${request.getType()}")
LdapConnection conn = getConn()
try {
bind(conn)
Optional<String> mxid = lookup(conn, request.getType(), request.getThreePid())
if (mxid.isPresent()) {
return Optional.of(new SingleLookupReply(request, mxid.get()));
}
} finally {
conn.close()
}
log.info("No match found")
return Optional.empty()
}
@Override
List<ThreePidMapping> populate(List<ThreePidMapping> mappings) {
log.info("Looking up {} mappings", mappings.size())
List<ThreePidMapping> mappingsFound = new ArrayList<>()
LdapConnection conn = getConn()
try {
bind(conn)
for (ThreePidMapping mapping : mappings) {
try {
Optional<String> mxid = lookup(conn, mapping.getMedium(), mapping.getValue())
if (mxid.isPresent()) {
mapping.setMxid(mxid.get())
mappingsFound.add(mapping)
}
} catch (IllegalArgumentException e) {
log.warn("{} is not a supported 3PID type for LDAP lookup", mapping.getMedium())
}
}
} finally {
conn.close()
}
return mappingsFound
}
}