Auth support with synapse REST auth module
This commit is contained in:
@@ -99,41 +99,85 @@ lookup:
|
||||
|
||||
|
||||
ldap:
|
||||
enabled: false
|
||||
tls: false
|
||||
host: 'localhost'
|
||||
port: 389
|
||||
bindDn: 'CN=Matrix Identity Server,CN=Users,DC=example,DC=org'
|
||||
bindPassword: 'password'
|
||||
baseDn: 'CN=Users,DC=example,DC=org'
|
||||
|
||||
# How should we resolve the Matrix ID in case of a match using the attribute.
|
||||
# Global enable/disable switch
|
||||
enabled: true
|
||||
|
||||
# Connection configuration to the LDAP server
|
||||
connection:
|
||||
|
||||
# If the connection should be secure
|
||||
tls: false
|
||||
|
||||
# Host to connect to
|
||||
host: 'localhost'
|
||||
|
||||
# Port to connect to
|
||||
port: 389
|
||||
|
||||
# Bind DN to use when performing lookups
|
||||
bindDn: 'CN=Matrix Identity Server,CN=Users,DC=example,DC=org'
|
||||
|
||||
# Bind password to use
|
||||
bindPassword: 'password'
|
||||
|
||||
# Base DN used in all queries
|
||||
baseDn: 'CN=Users,DC=example,DC=org'
|
||||
|
||||
# How to map Matrix attributes with LDAP attributes when performing lookup/auth
|
||||
attributes:
|
||||
|
||||
# The username/login that will be looked up or used to build Matrix IDs
|
||||
uid:
|
||||
|
||||
# How should we resolve the Matrix ID in case of a match using the attribute.
|
||||
#
|
||||
# The following type are supported:
|
||||
# - uid : the attribute only contains the UID part of the Matrix ID. e.g. 'john.doe' in @john.doe:example.org
|
||||
# - mxid : the attribute contains the full Matrix ID - e.g. '@john.doe:example.org'
|
||||
type: 'uid'
|
||||
|
||||
# The attribute containing the binding itself. This value will be used differently depending on the type.
|
||||
#
|
||||
# /!\ This should match the synapse LDAP Authenticator 'uid' configuration /!\
|
||||
#
|
||||
# Typical values:
|
||||
# - For type 'uid': 'userPrincipalName' or 'uid' or 'saMAccountName'
|
||||
# - For type 'mxid', regardless of the directory type, we recommend using 'pager' as it is a standard attribute and
|
||||
# is typically not used.
|
||||
value: 'userPrincipalName'
|
||||
|
||||
# The display name of the user
|
||||
name: 'displayName'
|
||||
|
||||
# Configuration section relating the authentication of users performed via LDAP.
|
||||
#
|
||||
# The following type are supported:
|
||||
# - uid : the attribute only contains the UID part of the Matrix ID. e.g. 'john.doe' in @john.doe:example.org
|
||||
# - mxid : the attribute contains the full Matrix ID - e.g. '@john.doe:example.org'
|
||||
type: 'uid'
|
||||
# This can be done using the REST Auth module for synapse and pointing it to the identity server.
|
||||
# See https://github.com/maxidor/matrix-synapse-rest-auth
|
||||
auth:
|
||||
|
||||
# The attribute containing the binding itself. This value will be used differently depending on the type.
|
||||
#
|
||||
# /!\ This should match the synapse LDAP Authenticator 'uid' configuration /!\
|
||||
#
|
||||
# Typical values:
|
||||
# - For type 'uid': 'userPrincipalName' or 'uid' or 'saMAccountName'
|
||||
# - For type 'mxid', regardless of the directory type, we recommend using 'pager' as it is a standard attribute and
|
||||
# is typically not used.
|
||||
attribute: 'userPrincipalName'
|
||||
|
||||
# Configure each 3PID type with a dedicated query.
|
||||
mappings:
|
||||
email: "(|(mailPrimaryAddress=%3pid)(mail=%3pid)(otherMailbox=%3pid))"
|
||||
|
||||
# Phone numbers query.
|
||||
# What to filter potential users by, typically by using a dedicated group.
|
||||
# If this value is not set, login check will be performed for all entities within the LDAP
|
||||
#
|
||||
# Phone numbers use the MSISDN format: https://en.wikipedia.org/wiki/MSISDN
|
||||
# This format does not include international prefix (+ or 00) and therefore has to be put in the query.
|
||||
# Adapt this to your needs for each attribute.
|
||||
msisdn: "(|(telephoneNumber=+%3pid)(mobile=+%3pid)(homePhone=+%3pid)(otherTelephone=+%3pid)(otherMobile=+%3pid)(otherHomePhone=+%3pid))"
|
||||
# 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
|
||||
identity:
|
||||
|
||||
# Configure each 3PID type with a dedicated query.
|
||||
medium:
|
||||
# E-mail query
|
||||
email: "(|(mailPrimaryAddress=%3pid)(mail=%3pid)(otherMailbox=%3pid))"
|
||||
|
||||
# Phone numbers query
|
||||
#
|
||||
# Phone numbers use the MSISDN format: https://en.wikipedia.org/wiki/MSISDN
|
||||
# This format does not include international prefix (+ or 00) and therefore has to be put in the query.
|
||||
# Adapt this to your needs for each attribute.
|
||||
msisdn: "(|(telephoneNumber=+%3pid)(mobile=+%3pid)(homePhone=+%3pid)(otherTelephone=+%3pid)(otherMobile=+%3pid)(otherHomePhone=+%3pid))"
|
||||
|
||||
|
||||
|
||||
|
@@ -62,6 +62,7 @@ buildscript {
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven { url "https://kamax.io/maven/releases/" }
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
@@ -75,6 +76,9 @@ dependencies {
|
||||
// Spring Boot - standalone app
|
||||
compile 'org.springframework.boot:spring-boot-starter-web:1.5.3.RELEASE'
|
||||
|
||||
// Matrix Java SDK
|
||||
compile 'io.kamax:matrix-java-sdk:0.0.2'
|
||||
|
||||
// ed25519 handling
|
||||
compile 'net.i2p.crypto:eddsa:0.1.0'
|
||||
|
||||
@@ -93,6 +97,9 @@ dependencies {
|
||||
// Phone numbers validation
|
||||
compile 'com.googlecode.libphonenumber:libphonenumber:8.7.1'
|
||||
|
||||
// Google Firebase Authentication backend
|
||||
compile 'com.google.firebase:firebase-admin:5.3.0'
|
||||
|
||||
testCompile 'junit:junit:4.12'
|
||||
}
|
||||
|
||||
|
7
src/main/groovy/io/kamax/mxisd/GlobalProvider.java
Normal file
7
src/main/groovy/io/kamax/mxisd/GlobalProvider.java
Normal file
@@ -0,0 +1,7 @@
|
||||
package io.kamax.mxisd;
|
||||
|
||||
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
|
||||
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
|
||||
|
||||
public interface GlobalProvider extends AuthenticatorProvider, IThreePidProvider {
|
||||
}
|
35
src/main/groovy/io/kamax/mxisd/auth/AuthManager.java
Normal file
35
src/main/groovy/io/kamax/mxisd/auth/AuthManager.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package io.kamax.mxisd.auth;
|
||||
|
||||
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class AuthManager {
|
||||
|
||||
private Logger log = LoggerFactory.getLogger(AuthManager.class);
|
||||
|
||||
@Autowired
|
||||
private List<AuthenticatorProvider> providers = new ArrayList<>();
|
||||
|
||||
public UserAuthResult authenticate(String id, String password) {
|
||||
for (AuthenticatorProvider provider : providers) {
|
||||
if (!provider.isEnabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
UserAuthResult result = provider.authenticate(id, password);
|
||||
if (result.isSuccess()) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return new UserAuthResult().failure();
|
||||
}
|
||||
|
||||
}
|
49
src/main/groovy/io/kamax/mxisd/auth/UserAuthResult.java
Normal file
49
src/main/groovy/io/kamax/mxisd/auth/UserAuthResult.java
Normal file
@@ -0,0 +1,49 @@
|
||||
package io.kamax.mxisd.auth;
|
||||
|
||||
public class UserAuthResult {
|
||||
|
||||
private boolean success;
|
||||
private String mxid;
|
||||
private String displayName;
|
||||
|
||||
public UserAuthResult failure() {
|
||||
success = false;
|
||||
mxid = null;
|
||||
displayName = null;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserAuthResult success(String mxid, String displayName) {
|
||||
setSuccess(true);
|
||||
setMxid(mxid);
|
||||
setDisplayName(displayName);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public void setSuccess(boolean success) {
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
public String getMxid() {
|
||||
return mxid;
|
||||
}
|
||||
|
||||
public void setMxid(String mxid) {
|
||||
this.mxid = mxid;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
package io.kamax.mxisd.auth.provider;
|
||||
|
||||
import io.kamax.mxisd.auth.UserAuthResult;
|
||||
|
||||
public interface AuthenticatorProvider {
|
||||
|
||||
boolean isEnabled();
|
||||
|
||||
UserAuthResult authenticate(String id, String password);
|
||||
|
||||
}
|
@@ -0,0 +1,227 @@
|
||||
package io.kamax.mxisd.auth.provider
|
||||
|
||||
import com.google.firebase.FirebaseApp
|
||||
import com.google.firebase.FirebaseOptions
|
||||
import com.google.firebase.auth.*
|
||||
import com.google.firebase.internal.NonNull
|
||||
import com.google.firebase.tasks.OnFailureListener
|
||||
import com.google.firebase.tasks.OnSuccessListener
|
||||
import io.kamax.matrix.ThreePidMedium
|
||||
import io.kamax.mxisd.GlobalProvider
|
||||
import io.kamax.mxisd.auth.UserAuthResult
|
||||
import io.kamax.mxisd.lookup.SingleLookupRequest
|
||||
import io.kamax.mxisd.lookup.ThreePidMapping
|
||||
import org.apache.commons.lang.StringUtils
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.function.Consumer
|
||||
import java.util.regex.Matcher
|
||||
import java.util.regex.Pattern
|
||||
|
||||
public class GoogleFirebaseAuthenticator implements GlobalProvider {
|
||||
|
||||
private Logger log = LoggerFactory.getLogger(GoogleFirebaseAuthenticator.class);
|
||||
|
||||
private static final Pattern matrixIdLaxPattern = Pattern.compile("@(.*):(.+)");
|
||||
|
||||
private boolean isEnabled;
|
||||
private String domain;
|
||||
private FirebaseApp fbApp;
|
||||
private FirebaseAuth fbAuth;
|
||||
|
||||
public GoogleFirebaseAuthenticator(boolean isEnabled) {
|
||||
this.isEnabled = isEnabled;
|
||||
}
|
||||
|
||||
public GoogleFirebaseAuthenticator(String credsPath, String db, String domain) {
|
||||
this(true);
|
||||
this.domain = domain;
|
||||
try {
|
||||
fbApp = FirebaseApp.initializeApp(getOpts(credsPath, db));
|
||||
fbAuth = FirebaseAuth.getInstance(fbApp);
|
||||
|
||||
log.info("Google Firebase Authentication is ready");
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Error when initializing Firebase", e);
|
||||
}
|
||||
}
|
||||
|
||||
private FirebaseCredential getCreds(String credsPath) throws IOException {
|
||||
if (StringUtils.isNotBlank(credsPath)) {
|
||||
return FirebaseCredentials.fromCertificate(new FileInputStream(credsPath));
|
||||
} else {
|
||||
return FirebaseCredentials.applicationDefault();
|
||||
}
|
||||
}
|
||||
|
||||
private FirebaseOptions getOpts(String credsPath, String db) throws IOException {
|
||||
if (StringUtils.isBlank(db)) {
|
||||
throw new IllegalArgumentException("Firebase database is not configured");
|
||||
}
|
||||
|
||||
return new FirebaseOptions.Builder()
|
||||
.setCredential(getCreds(credsPath))
|
||||
.setDatabaseUrl(db)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return isEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLocal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return 25;
|
||||
}
|
||||
|
||||
private void waitOnLatch(CountDownLatch l) {
|
||||
try {
|
||||
l.await(30, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("Interrupted while waiting for Firebase auth check");
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<UserRecord> findInternal(String medium, String address) {
|
||||
UserRecord r;
|
||||
CountDownLatch l = new CountDownLatch(1);
|
||||
|
||||
OnSuccessListener<UserRecord> success = new OnSuccessListener<UserRecord>() {
|
||||
@Override
|
||||
void onSuccess(UserRecord result) {
|
||||
log.info("Found 3PID match for {}:{} - UID is {}", medium, address, result.getUid())
|
||||
r = result;
|
||||
l.countDown()
|
||||
}
|
||||
};
|
||||
|
||||
OnFailureListener failure = new OnFailureListener() {
|
||||
@Override
|
||||
void onFailure(@NonNull Exception e) {
|
||||
log.info("No 3PID match for {}:{} - {}", medium, address, e.getMessage())
|
||||
r = null;
|
||||
l.countDown()
|
||||
}
|
||||
};
|
||||
|
||||
if (ThreePidMedium.Email.is(medium)) {
|
||||
log.info("Performing E-mail 3PID lookup for {}", address)
|
||||
fbAuth.getUserByEmail(address)
|
||||
.addOnSuccessListener(success)
|
||||
.addOnFailureListener(failure);
|
||||
waitOnLatch(l);
|
||||
} else if (ThreePidMedium.PhoneNumber.is(medium)) {
|
||||
log.info("Performing msisdn 3PID lookup for {}", address)
|
||||
fbAuth.getUserByPhoneNumber(address)
|
||||
.addOnSuccessListener(success)
|
||||
.addOnFailureListener(failure);
|
||||
waitOnLatch(l);
|
||||
} else {
|
||||
log.info("{} is not a supported 3PID medium", medium);
|
||||
r = null;
|
||||
}
|
||||
|
||||
return Optional.ofNullable(r);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<?> find(SingleLookupRequest request) {
|
||||
Optional<UserRecord> urOpt = findInternal(request.getType(), request.getThreePid())
|
||||
if (urOpt.isPresent()) {
|
||||
return [
|
||||
address : request.getThreePid(),
|
||||
medium : request.getType(),
|
||||
mxid : "@${urOpt.get().getUid()}:${domain}",
|
||||
not_before: 0,
|
||||
not_after : 9223372036854775807,
|
||||
ts : 0
|
||||
]
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ThreePidMapping> populate(List<ThreePidMapping> mappings) {
|
||||
List<ThreePidMapping> results = new ArrayList<>();
|
||||
mappings.parallelStream().forEach(new Consumer<ThreePidMapping>() {
|
||||
@Override
|
||||
void accept(ThreePidMapping o) {
|
||||
Optional<UserRecord> urOpt = findInternal(o.getMedium(), o.getValue());
|
||||
if (urOpt.isPresent()) {
|
||||
ThreePidMapping result = new ThreePidMapping();
|
||||
result.setMedium(o.getMedium())
|
||||
result.setValue(o.getValue())
|
||||
result.setMxid("@${urOpt.get().getUid()}:${domain}")
|
||||
results.add(result)
|
||||
}
|
||||
}
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserAuthResult authenticate(String id, String password) {
|
||||
if (!isEnabled()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
final UserAuthResult result = new UserAuthResult();
|
||||
|
||||
log.info("Trying to authenticate {}", id);
|
||||
Matcher m = matrixIdLaxPattern.matcher(id);
|
||||
if (!m.matches()) {
|
||||
log.warn("Could not validate {} as a Matrix ID", id);
|
||||
result.failure();
|
||||
}
|
||||
|
||||
String localpart = m.group(1);
|
||||
|
||||
CountDownLatch l = new CountDownLatch(1);
|
||||
fbAuth.verifyIdToken(password).addOnSuccessListener(new OnSuccessListener<FirebaseToken>() {
|
||||
@Override
|
||||
void onSuccess(FirebaseToken token) {
|
||||
if (!StringUtils.equals(localpart, token.getUid())) {
|
||||
log.info("Failture to authenticate {}: Matrix ID localpart '{}' does not match Firebase UID '{}'", id, localpart, token.getUid());
|
||||
result.failure();
|
||||
}
|
||||
|
||||
log.info("{} was successfully authenticated", id);
|
||||
result.success(id, token.getName());
|
||||
l.countDown()
|
||||
}
|
||||
}).addOnFailureListener(new OnFailureListener() {
|
||||
@Override
|
||||
void onFailure(@NonNull Exception e) {
|
||||
if (e instanceof IllegalArgumentException) {
|
||||
log.info("Failure to authenticate {}: invalid firebase token", id);
|
||||
} else {
|
||||
log.info("Failure to authenticate {}: {}", id, e.getMessage(), e);
|
||||
log.info("Exception", e);
|
||||
}
|
||||
|
||||
result.failure();
|
||||
l.countDown()
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
l.await(30, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("Interrupted while waiting for Firebase auth check");
|
||||
result.failure();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
71
src/main/groovy/io/kamax/mxisd/config/FirebaseConfig.java
Normal file
71
src/main/groovy/io/kamax/mxisd/config/FirebaseConfig.java
Normal file
@@ -0,0 +1,71 @@
|
||||
package io.kamax.mxisd.config;
|
||||
|
||||
import io.kamax.mxisd.GlobalProvider;
|
||||
import io.kamax.mxisd.auth.provider.GoogleFirebaseAuthenticator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties("firebase")
|
||||
public class FirebaseConfig {
|
||||
|
||||
private Logger log = LoggerFactory.getLogger(FirebaseConfig.class);
|
||||
|
||||
@Autowired
|
||||
private ServerConfig srvCfg;
|
||||
|
||||
private boolean enabled;
|
||||
private String credentials;
|
||||
private String database;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getCredentials() {
|
||||
return credentials;
|
||||
}
|
||||
|
||||
public void setCredentials(String credentials) {
|
||||
this.credentials = credentials;
|
||||
}
|
||||
|
||||
public String getDatabase() {
|
||||
return database;
|
||||
}
|
||||
|
||||
public void setDatabase(String database) {
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
private void postConstruct() {
|
||||
log.info("--- Firebase configuration ---");
|
||||
log.info("Enabled: {}", isEnabled());
|
||||
if (isEnabled()) {
|
||||
log.info("Credentials: {}", getCredentials());
|
||||
log.info("Database: {}", getDatabase());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GlobalProvider getProvider() {
|
||||
if (!enabled) {
|
||||
return new GoogleFirebaseAuthenticator(false);
|
||||
} else {
|
||||
return new GoogleFirebaseAuthenticator(credentials, database, srvCfg.getName());
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,156 +0,0 @@
|
||||
/*
|
||||
* 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.config
|
||||
|
||||
import org.apache.commons.lang.StringUtils
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.beans.factory.InitializingBean
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||
import org.springframework.context.annotation.Configuration
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "ldap")
|
||||
class LdapConfig implements InitializingBean {
|
||||
|
||||
private Logger log = LoggerFactory.getLogger(LdapConfig.class)
|
||||
|
||||
private boolean enabled
|
||||
private boolean tls
|
||||
private String host
|
||||
private int port
|
||||
private String baseDn
|
||||
private String type
|
||||
private String attribute
|
||||
private String bindDn
|
||||
private String bindPassword
|
||||
private Map<String, String> mappings
|
||||
|
||||
boolean getEnabled() {
|
||||
return enabled
|
||||
}
|
||||
|
||||
void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled
|
||||
}
|
||||
|
||||
boolean getTls() {
|
||||
return tls
|
||||
}
|
||||
|
||||
void setTls(boolean tls) {
|
||||
this.tls = tls
|
||||
}
|
||||
|
||||
String getHost() {
|
||||
return host
|
||||
}
|
||||
|
||||
void setHost(String host) {
|
||||
this.host = host
|
||||
}
|
||||
|
||||
int getPort() {
|
||||
return port
|
||||
}
|
||||
|
||||
void setPort(int port) {
|
||||
this.port = port
|
||||
}
|
||||
|
||||
String getBaseDn() {
|
||||
return baseDn
|
||||
}
|
||||
|
||||
void setBaseDn(String baseDn) {
|
||||
this.baseDn = baseDn
|
||||
}
|
||||
|
||||
String getType() {
|
||||
return type
|
||||
}
|
||||
|
||||
void setType(String type) {
|
||||
this.type = type
|
||||
}
|
||||
|
||||
String getAttribute() {
|
||||
return attribute
|
||||
}
|
||||
|
||||
void setAttribute(String attribute) {
|
||||
this.attribute = attribute
|
||||
}
|
||||
|
||||
String getBindDn() {
|
||||
return bindDn
|
||||
}
|
||||
|
||||
void setBindDn(String bindDn) {
|
||||
this.bindDn = bindDn
|
||||
}
|
||||
|
||||
String getBindPassword() {
|
||||
return bindPassword
|
||||
}
|
||||
|
||||
void setBindPassword(String bindPassword) {
|
||||
this.bindPassword = bindPassword
|
||||
}
|
||||
|
||||
Map<String, String> getMappings() {
|
||||
return mappings
|
||||
}
|
||||
|
||||
void setMappings(Map<String, String> mappings) {
|
||||
this.mappings = mappings
|
||||
}
|
||||
|
||||
Optional<String> getMapping(String type) {
|
||||
if (mappings == null) {
|
||||
return Optional.empty()
|
||||
}
|
||||
|
||||
return Optional.ofNullable(mappings.get(type))
|
||||
}
|
||||
|
||||
@Override
|
||||
void afterPropertiesSet() throws Exception {
|
||||
log.info("LDAP enabled: {}", getEnabled())
|
||||
|
||||
if (!getEnabled()) {
|
||||
return
|
||||
}
|
||||
|
||||
log.info("Matrix ID type: {}", getType())
|
||||
log.info("LDAP Host: {}", getHost())
|
||||
log.info("LDAP Bind DN: {}", getBindDn())
|
||||
log.info("LDAP Attribute: {}", getAttribute())
|
||||
|
||||
if (StringUtils.isBlank(getHost())) {
|
||||
throw new IllegalStateException("LDAP Host must be configured!")
|
||||
}
|
||||
if (StringUtils.isBlank(getAttribute())) {
|
||||
throw new IllegalStateException("LDAP attribute must be configured!")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package io.kamax.mxisd.config.ldap;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "ldap.attribute")
|
||||
public class LdapAttributeConfig {
|
||||
|
||||
private LdapAttributeUidConfig uid;
|
||||
private String name;
|
||||
|
||||
public LdapAttributeUidConfig getUid() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
public void setUid(LdapAttributeUidConfig uid) {
|
||||
this.uid = uid;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
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 {
|
||||
|
||||
private String type;
|
||||
private String value;
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
package io.kamax.mxisd.config.ldap;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "ldap.auth")
|
||||
public class LdapAuthConfig {
|
||||
|
||||
private String filter;
|
||||
|
||||
public String getFilter() {
|
||||
return filter;
|
||||
}
|
||||
|
||||
public void setFilter(String filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
}
|
125
src/main/groovy/io/kamax/mxisd/config/ldap/LdapConfig.groovy
Normal file
125
src/main/groovy/io/kamax/mxisd/config/ldap/LdapConfig.groovy
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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.config.ldap
|
||||
|
||||
import groovy.json.JsonOutput
|
||||
import org.apache.commons.lang.StringUtils
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||
import org.springframework.context.annotation.Configuration
|
||||
|
||||
import javax.annotation.PostConstruct
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "ldap")
|
||||
class LdapConfig {
|
||||
|
||||
private Logger log = LoggerFactory.getLogger(LdapConfig.class)
|
||||
|
||||
private boolean enabled
|
||||
|
||||
@Autowired
|
||||
private LdapConnectionConfig conn
|
||||
private LdapAttributeConfig attribute
|
||||
private LdapAuthConfig auth
|
||||
private LdapIdentityConfig identity
|
||||
|
||||
boolean isEnabled() {
|
||||
return enabled
|
||||
}
|
||||
|
||||
void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled
|
||||
}
|
||||
|
||||
LdapConnectionConfig getConn() {
|
||||
return conn
|
||||
}
|
||||
|
||||
void setConn(LdapConnectionConfig conn) {
|
||||
this.conn = conn
|
||||
}
|
||||
|
||||
LdapAttributeConfig getAttribute() {
|
||||
return attribute
|
||||
}
|
||||
|
||||
void setAttribute(LdapAttributeConfig attribute) {
|
||||
this.attribute = attribute
|
||||
}
|
||||
|
||||
LdapAuthConfig getAuth() {
|
||||
return auth
|
||||
}
|
||||
|
||||
void setAuth(LdapAuthConfig auth) {
|
||||
this.auth = auth
|
||||
}
|
||||
|
||||
LdapIdentityConfig getIdentity() {
|
||||
return identity
|
||||
}
|
||||
|
||||
void setIdentity(LdapIdentityConfig identity) {
|
||||
this.identity = identity
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
void afterPropertiesSet() {
|
||||
log.info("--- LDAP Config ---")
|
||||
log.info("Enabled: {}", isEnabled())
|
||||
|
||||
if (!isEnabled()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(conn.getHost())) {
|
||||
throw new IllegalStateException("LDAP Host must be configured!")
|
||||
}
|
||||
|
||||
if (1 > conn.getPort() || 65535 < conn.getPort()) {
|
||||
throw new IllegalStateException("LDAP port is not valid")
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(attribute.getUid().getType())) {
|
||||
throw new IllegalStateException("Attribute UID Type cannot be empty")
|
||||
}
|
||||
|
||||
|
||||
if (StringUtils.isBlank(attribute.getUid().getValue())) {
|
||||
throw new IllegalStateException("Attribute UID value cannot be empty")
|
||||
}
|
||||
|
||||
|
||||
log.info("Conn: {}", JsonOutput.toJson(conn))
|
||||
log.info("Host: {}", conn.getHost())
|
||||
log.info("Port: {}", conn.getPort())
|
||||
log.info("Bind DN: {}", conn.getBindDn())
|
||||
log.info("Base DN: {}", conn.getBaseDn())
|
||||
|
||||
log.info("Attribute: {}", JsonOutput.toJson(attribute))
|
||||
log.info("Auth: {}", JsonOutput.toJson(auth))
|
||||
log.info("Identity: {}", JsonOutput.toJson(identity))
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
package io.kamax.mxisd.config.ldap;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "ldap.connection")
|
||||
public class LdapConnectionConfig {
|
||||
|
||||
private boolean tls;
|
||||
private String host;
|
||||
private int port;
|
||||
private String bindDn;
|
||||
private String bindPassword;
|
||||
private String baseDn;
|
||||
|
||||
public boolean isTls() {
|
||||
return tls;
|
||||
}
|
||||
|
||||
public void setTls(boolean tls) {
|
||||
this.tls = tls;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public String getBindDn() {
|
||||
return bindDn;
|
||||
}
|
||||
|
||||
public void setBindDn(String bindDn) {
|
||||
this.bindDn = bindDn;
|
||||
}
|
||||
|
||||
public String getBindPassword() {
|
||||
return bindPassword;
|
||||
}
|
||||
|
||||
public void setBindPassword(String bindPassword) {
|
||||
this.bindPassword = bindPassword;
|
||||
}
|
||||
|
||||
public String getBaseDn() {
|
||||
return baseDn;
|
||||
}
|
||||
|
||||
public void setBaseDn(String baseDn) {
|
||||
this.baseDn = baseDn;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
package io.kamax.mxisd.config.ldap;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "ldap.identity")
|
||||
public class LdapIdentityConfig {
|
||||
|
||||
private Map<String, String> medium = new HashMap<>();
|
||||
|
||||
public Map<String, String> getMedium() {
|
||||
return medium;
|
||||
}
|
||||
|
||||
public Optional<String> getQuery(String key) {
|
||||
return Optional.ofNullable(medium.get(key));
|
||||
}
|
||||
|
||||
public void setMedium(Map<String, String> medium) {
|
||||
this.medium = medium;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.controller.v1;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import io.kamax.mxisd.auth.AuthManager;
|
||||
import io.kamax.mxisd.auth.UserAuthResult;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@RestController
|
||||
@CrossOrigin
|
||||
@RequestMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public class AuthController {
|
||||
|
||||
private Logger log = LoggerFactory.getLogger(AuthController.class);
|
||||
|
||||
private Gson gson = new Gson();
|
||||
|
||||
@Autowired
|
||||
private AuthManager mgr;
|
||||
|
||||
@RequestMapping(value = "/_matrix-internal/identity/v1/check_credentials", method = RequestMethod.POST)
|
||||
public String checkCredentials(HttpServletRequest req) {
|
||||
try {
|
||||
JsonElement el = new JsonParser().parse(IOUtils.toString(req.getInputStream(), StandardCharsets.UTF_8));
|
||||
if (!el.isJsonObject() || !el.getAsJsonObject().has("user")) {
|
||||
throw new IllegalArgumentException("Missing user key");
|
||||
}
|
||||
|
||||
JsonObject authData = el.getAsJsonObject().get("user").getAsJsonObject();
|
||||
if (!authData.has("id") || !authData.has("password")) {
|
||||
throw new IllegalArgumentException("Missing id or password keys");
|
||||
}
|
||||
|
||||
String id = authData.get("id").getAsString();
|
||||
log.info("Requested to check credentials for {}", id);
|
||||
String password = authData.get("password").getAsString();
|
||||
|
||||
UserAuthResult result = mgr.authenticate(id, password);
|
||||
|
||||
JsonObject authObj = new JsonObject();
|
||||
authObj.addProperty("success", result.isSuccess());
|
||||
if (result.isSuccess()) {
|
||||
authObj.addProperty("mxid", result.getMxid());
|
||||
authObj.addProperty("display_name", result.getDisplayName());
|
||||
}
|
||||
JsonObject obj = new JsonObject();
|
||||
|
||||
obj.add("authentication", authObj);
|
||||
return gson.toJson(obj);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package io.kamax.mxisd.controller.v1;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@CrossOrigin
|
||||
@RequestMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public class StatusController {
|
||||
|
||||
@RequestMapping(value = "/_matrix/identity/status")
|
||||
public String getStatus() {
|
||||
// TODO link to backend
|
||||
return "{\"status\":{\"health\":\"OK\"}}";
|
||||
}
|
||||
|
||||
}
|
@@ -20,8 +20,8 @@
|
||||
|
||||
package io.kamax.mxisd.lookup.provider
|
||||
|
||||
import io.kamax.mxisd.config.LdapConfig
|
||||
import io.kamax.mxisd.config.ServerConfig
|
||||
import io.kamax.mxisd.config.ldap.LdapConfig
|
||||
import io.kamax.mxisd.lookup.SingleLookupRequest
|
||||
import io.kamax.mxisd.lookup.ThreePidMapping
|
||||
import org.apache.commons.lang.StringUtils
|
||||
@@ -53,7 +53,19 @@ class LdapProvider implements IThreePidProvider {
|
||||
|
||||
@Override
|
||||
boolean isEnabled() {
|
||||
return ldapCfg.getEnabled()
|
||||
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())
|
||||
}
|
||||
|
||||
private String getUidAttribute() {
|
||||
return ldapCfg.getAttribute().getUid().getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -67,20 +79,22 @@ class LdapProvider implements IThreePidProvider {
|
||||
}
|
||||
|
||||
Optional<String> lookup(LdapConnection conn, String medium, String value) {
|
||||
Optional<String> queryOpt = ldapCfg.getMapping(medium)
|
||||
String uidAttribute = getUidAttribute()
|
||||
|
||||
Optional<String> queryOpt = ldapCfg.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.getBaseDn(), searchQuery, SearchScope.SUBTREE, ldapCfg.getAttribute())
|
||||
EntryCursor cursor = conn.search(ldapCfg.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(ldapCfg.getAttribute())
|
||||
Attribute attribute = entry.get(uidAttribute)
|
||||
if (attribute == null) {
|
||||
log.info("DN {}: no attribute {}, skpping", entry.getDn(), ldapCfg.getAttribute())
|
||||
continue
|
||||
@@ -94,12 +108,13 @@ class LdapProvider implements IThreePidProvider {
|
||||
|
||||
StringBuilder matrixId = new StringBuilder()
|
||||
// TODO Should we turn this block into a map of functions?
|
||||
if (StringUtils.equals(UID, ldapCfg.getType())) {
|
||||
String uidType = ldapCfg.getAttribute().getUid().getType()
|
||||
if (StringUtils.equals(UID, uidType)) {
|
||||
matrixId.append("@").append(data).append(":").append(srvCfg.getName())
|
||||
} else if (StringUtils.equals(MATRIX_ID, ldapCfg.getType())) {
|
||||
} else if (StringUtils.equals(MATRIX_ID, uidType)) {
|
||||
matrixId.append(data)
|
||||
} else {
|
||||
log.warn("Bind was found but type {} is not supported", ldapCfg.getType())
|
||||
log.warn("Bind was found but type {} is not supported", uidType)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -119,9 +134,9 @@ class LdapProvider implements IThreePidProvider {
|
||||
Optional<?> find(SingleLookupRequest request) {
|
||||
log.info("Performing LDAP lookup ${request.getThreePid()} of type ${request.getType()}")
|
||||
|
||||
LdapConnection conn = new LdapNetworkConnection(ldapCfg.getHost(), ldapCfg.getPort(), ldapCfg.getTls())
|
||||
LdapConnection conn = getConn()
|
||||
try {
|
||||
conn.bind(ldapCfg.getBindDn(), ldapCfg.getBindPassword())
|
||||
bind(conn)
|
||||
|
||||
Optional<String> mxid = lookup(conn, request.getType(), request.getThreePid())
|
||||
if (mxid.isPresent()) {
|
||||
@@ -147,9 +162,9 @@ class LdapProvider implements IThreePidProvider {
|
||||
log.info("Looking up {} mappings", mappings.size())
|
||||
List<ThreePidMapping> mappingsFound = new ArrayList<>()
|
||||
|
||||
LdapConnection conn = new LdapNetworkConnection(ldapCfg.getHost(), ldapCfg.getPort())
|
||||
LdapConnection conn = getConn()
|
||||
try {
|
||||
conn.bind(ldapCfg.getBindDn(), ldapCfg.getBindPassword())
|
||||
bind(conn)
|
||||
|
||||
for (ThreePidMapping mapping : mappings) {
|
||||
try {
|
||||
|
Reference in New Issue
Block a user