Search in sources :

Example 1 with EntryCursor

use of org.apache.directory.api.ldap.model.cursor.EntryCursor in project graylog2-server by Graylog2.

the class LdapConnector method search.

@Nullable
public LdapEntry search(LdapNetworkConnection connection, String searchBase, String searchPattern, String displayNameAttribute, String principal, boolean activeDirectory, String groupSearchBase, String groupIdAttribute, String groupSearchPattern) throws LdapException, CursorException {
    final LdapEntry ldapEntry = new LdapEntry();
    final Set<String> groupDns = Sets.newHashSet();
    final String filter = new MessageFormat(searchPattern, Locale.ENGLISH).format(new Object[] { sanitizePrincipal(principal) });
    if (LOG.isTraceEnabled()) {
        LOG.trace("Search {} for {}, starting at {}", activeDirectory ? "ActiveDirectory" : "LDAP", filter, searchBase);
    }
    try (final EntryCursor entryCursor = connection.search(searchBase, filter, SearchScope.SUBTREE, groupIdAttribute, displayNameAttribute, "dn", "uid", "userPrincipalName", "mail", "rfc822Mailbox", "memberOf", "isMemberOf")) {
        final Iterator<Entry> it = entryCursor.iterator();
        if (it.hasNext()) {
            final Entry e = it.next();
            // always set the proper DN for the entry, we need it for group matching
            ldapEntry.setDn(e.getDn().getName());
            // for generic LDAP use the dn of the entry for the subsequent bind, active directory needs the userPrincipalName attribute (set below)
            if (!activeDirectory) {
                ldapEntry.setBindPrincipal(e.getDn().getName());
            }
            for (Attribute attribute : e.getAttributes()) {
                if (activeDirectory && "userPrincipalName".equalsIgnoreCase(attribute.getId())) {
                    ldapEntry.setBindPrincipal(attribute.getString());
                }
                if (attribute.isHumanReadable()) {
                    ldapEntry.put(attribute.getId(), Joiner.on(", ").join(attribute.iterator()));
                }
                // ActiveDirectory (memberOf) and Sun Directory Server (isMemberOf)
                if ("memberOf".equalsIgnoreCase(attribute.getId()) || "isMemberOf".equalsIgnoreCase(attribute.getId())) {
                    for (Value<?> group : attribute) {
                        groupDns.add(group.getString());
                    }
                }
            }
        } else {
            LOG.trace("No LDAP entry found for filter {}", filter);
            return null;
        }
        if (!groupDns.isEmpty() && !isNullOrEmpty(groupSearchBase) && !isNullOrEmpty(groupIdAttribute)) {
            // according to groupIdAttribute if present
            try {
                for (String groupDn : groupDns) {
                    LOG.trace("Looking up group {}", groupDn);
                    try {
                        Entry group = connection.lookup(groupDn, groupIdAttribute);
                        // See: https://github.com/Graylog2/graylog2-server/issues/1453
                        if (group != null) {
                            final Attribute groupId = group.get(groupIdAttribute);
                            LOG.trace("Resolved {} to group {}", groupDn, groupId);
                            if (groupId != null) {
                                final String string = groupId.getString();
                                ldapEntry.addGroups(Collections.singleton(string));
                            }
                        } else {
                            LOG.debug("Unable to lookup group: {}", groupDn);
                        }
                    } catch (LdapException e) {
                        LOG.warn("Error while looking up group " + groupDn, e);
                    }
                }
            } catch (Exception e) {
                LOG.error("Unexpected error during LDAP group resolution", e);
            }
        }
        if (ldapEntry.getGroups().isEmpty() && !isNullOrEmpty(groupSearchBase) && !isNullOrEmpty(groupIdAttribute) && !isNullOrEmpty(groupSearchPattern)) {
            ldapEntry.addGroups(findGroups(connection, groupSearchBase, groupSearchPattern, groupIdAttribute, ldapEntry));
            LOG.trace("LDAP search found entry for DN {} with search filter {}: {}", ldapEntry.getDn(), filter, ldapEntry);
        } else {
            if (groupDns.isEmpty()) {
                LOG.info("LDAP group search base, id attribute or object class missing, not iterating over LDAP groups.");
            }
        }
        return ldapEntry;
    } catch (IOException e) {
        LOG.debug("Error while closing cursor", e);
        return null;
    }
}
Also used : EntryCursor(org.apache.directory.api.ldap.model.cursor.EntryCursor) Entry(org.apache.directory.api.ldap.model.entry.Entry) LdapEntry(org.graylog2.shared.security.ldap.LdapEntry) MessageFormat(java.text.MessageFormat) Attribute(org.apache.directory.api.ldap.model.entry.Attribute) LdapEntry(org.graylog2.shared.security.ldap.LdapEntry) IOException(java.io.IOException) LdapException(org.apache.directory.api.ldap.model.exception.LdapException) CursorException(org.apache.directory.api.ldap.model.cursor.CursorException) UncheckedTimeoutException(com.google.common.util.concurrent.UncheckedTimeoutException) IOException(java.io.IOException) LdapInvalidDnException(org.apache.directory.api.ldap.model.exception.LdapInvalidDnException) LdapException(org.apache.directory.api.ldap.model.exception.LdapException) Nullable(javax.annotation.Nullable)

Example 2 with EntryCursor

use of org.apache.directory.api.ldap.model.cursor.EntryCursor in project graylog2-server by Graylog2.

the class LdapConnector method findGroups.

public Set<String> findGroups(LdapNetworkConnection connection, String groupSearchBase, String groupSearchPattern, String groupIdAttribute, @Nullable LdapEntry ldapEntry) {
    final Set<String> groups = Sets.newHashSet();
    try (final EntryCursor groupSearch = connection.search(groupSearchBase, groupSearchPattern, SearchScope.SUBTREE, "objectClass", ATTRIBUTE_UNIQUE_MEMBER, ATTRIBUTE_MEMBER, ATTRIBUTE_MEMBER_UID, groupIdAttribute)) {
        LOG.trace("LDAP search for groups: {} starting at {}", groupSearchPattern, groupSearchBase);
        for (Entry e : groupSearch) {
            if (LOG.isTraceEnabled()) {
                LOG.trace("Group Entry: {}", e.toString("  "));
            }
            if (!e.containsAttribute(groupIdAttribute)) {
                LOG.warn("Unknown group id attribute {}, skipping group entry {}", groupIdAttribute, e);
                continue;
            }
            final String groupId = e.get(groupIdAttribute).getString();
            if (ldapEntry == null) {
                // no membership lookup possible (we have no user), simply collect the found group names
                groups.add(groupId);
            } else {
                // test if the given dn parameter is actually member of any of the found groups
                String memberAttribute;
                if (e.hasObjectClass("groupOfUniqueNames")) {
                    memberAttribute = ATTRIBUTE_UNIQUE_MEMBER;
                } else if (e.hasObjectClass("groupOfNames") || e.hasObjectClass("group")) {
                    memberAttribute = ATTRIBUTE_MEMBER;
                } else if (e.hasObjectClass("posixGroup")) {
                    memberAttribute = ATTRIBUTE_MEMBER_UID;
                } else {
                    // Trying auto detection of the member attribute. This should be configurable!
                    if (e.containsAttribute(ATTRIBUTE_UNIQUE_MEMBER)) {
                        memberAttribute = ATTRIBUTE_UNIQUE_MEMBER;
                    } else if (e.containsAttribute(ATTRIBUTE_MEMBER_UID)) {
                        memberAttribute = ATTRIBUTE_MEMBER_UID;
                    } else {
                        memberAttribute = ATTRIBUTE_MEMBER;
                    }
                    LOG.warn("Unable to auto-detect the LDAP group object class, assuming '{}' is the correct attribute.", memberAttribute);
                }
                final Attribute members = e.get(memberAttribute);
                if (members != null) {
                    final String dn = normalizedDn(ldapEntry.getDn());
                    final String uid = ldapEntry.get("uid");
                    for (Value<?> member : members) {
                        LOG.trace("DN {} == {} member?", dn, member.getString());
                        if (dn != null && dn.equalsIgnoreCase(normalizedDn(member.getString()))) {
                            groups.add(groupId);
                        } else {
                            // check against the uid attribute of the user.
                            if (!isNullOrEmpty(uid) && uid.equalsIgnoreCase(member.getString())) {
                                LOG.trace("UID {} == {} member?", uid, member.getString());
                                groups.add(groupId);
                            }
                        }
                    }
                }
            }
        }
    } catch (Exception e) {
        LOG.warn("Unable to iterate over user's groups, unable to perform group mapping. Graylog does not support " + "LDAP referrals at the moment. Please see " + DocsHelper.PAGE_LDAP_TROUBLESHOOTING.toString() + " for more information.", ExceptionUtils.getRootCause(e));
    }
    return groups;
}
Also used : EntryCursor(org.apache.directory.api.ldap.model.cursor.EntryCursor) Entry(org.apache.directory.api.ldap.model.entry.Entry) LdapEntry(org.graylog2.shared.security.ldap.LdapEntry) Attribute(org.apache.directory.api.ldap.model.entry.Attribute) CursorException(org.apache.directory.api.ldap.model.cursor.CursorException) UncheckedTimeoutException(com.google.common.util.concurrent.UncheckedTimeoutException) IOException(java.io.IOException) LdapInvalidDnException(org.apache.directory.api.ldap.model.exception.LdapInvalidDnException) LdapException(org.apache.directory.api.ldap.model.exception.LdapException)

Example 3 with EntryCursor

use of org.apache.directory.api.ldap.model.cursor.EntryCursor in project mxisd by kamax-io.

the class LdapAuthProvider method authenticate.

@Override
public BackendAuthResult authenticate(_MatrixID mxid, String password) {
    log.info("Performing auth for {}", mxid);
    try (LdapConnection conn = getConn()) {
        bind(conn);
        String uidType = getAt().getUid().getType();
        String userFilterValue = StringUtils.equals(LdapBackend.UID, uidType) ? mxid.getLocalPart() : mxid.getId();
        if (StringUtils.isBlank(userFilterValue)) {
            log.warn("Username is empty, failing auth");
            return BackendAuthResult.failure();
        }
        String userFilter = "(" + getUidAtt() + "=" + userFilterValue + ")";
        userFilter = buildWithFilter(userFilter, getCfg().getAuth().getFilter());
        Set<String> attributes = new HashSet<>();
        attributes.add(getUidAtt());
        attributes.add(getAt().getName());
        getAt().getThreepid().forEach((k, v) -> attributes.addAll(v));
        String[] attArray = new String[attributes.size()];
        attributes.toArray(attArray);
        log.debug("Base DN: {}", getBaseDn());
        log.debug("Query: {}", userFilter);
        log.debug("Attributes: {}", GsonUtil.build().toJson(attArray));
        try (EntryCursor cursor = conn.search(getBaseDn(), userFilter, SearchScope.SUBTREE, attArray)) {
            while (cursor.next()) {
                Entry entry = cursor.get();
                String dn = entry.getDn().getName();
                log.info("Checking possible match, DN: {}", dn);
                if (!getAttribute(entry, getUidAtt()).isPresent()) {
                    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 BackendAuthResult.failure();
                }
                Attribute nameAttribute = entry.get(getAt().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);
                // TODO should we canonicalize the MXID?
                BackendAuthResult result = BackendAuthResult.success(mxid.getId(), UserIdType.MatrixID, name);
                log.info("Processing 3PIDs for profile");
                getAt().getThreepid().forEach((k, v) -> {
                    log.info("Processing 3PID type {}", k);
                    v.forEach(attId -> {
                        List<String> values = getAttributes(entry, attId);
                        log.info("\tAttribute {} has {} value(s)", attId, values.size());
                        getAttributes(entry, attId).forEach(tpidValue -> {
                            if (ThreePidMedium.PhoneNumber.is(k)) {
                                tpidValue = getMsisdn(tpidValue).orElse(tpidValue);
                            }
                            result.withThreePid(new ThreePid(k, tpidValue));
                        });
                    });
                });
                log.info("Found {} 3PIDs", result.getProfile().getThreePids().size());
                return result;
            }
        } catch (CursorLdapReferralException e) {
            log.warn("Entity for {} is only available via referral, skipping", mxid);
        }
        log.info("No match were found for {}", mxid);
        return BackendAuthResult.failure();
    } catch (LdapException | IOException | CursorException e) {
        throw new RuntimeException(e);
    }
}
Also used : EntryCursor(org.apache.directory.api.ldap.model.cursor.EntryCursor) Attribute(org.apache.directory.api.ldap.model.entry.Attribute) IOException(java.io.IOException) BackendAuthResult(io.kamax.mxisd.auth.provider.BackendAuthResult) Entry(org.apache.directory.api.ldap.model.entry.Entry) CursorException(org.apache.directory.api.ldap.model.cursor.CursorException) CursorLdapReferralException(org.apache.directory.api.ldap.model.cursor.CursorLdapReferralException) ThreePid(io.kamax.matrix.ThreePid) LdapException(org.apache.directory.api.ldap.model.exception.LdapException) LdapConnection(org.apache.directory.ldap.client.api.LdapConnection) HashSet(java.util.HashSet)

Example 4 with EntryCursor

use of org.apache.directory.api.ldap.model.cursor.EntryCursor in project mxisd by kamax-io.

the class LdapDirectoryProvider method search.

protected UserDirectorySearchResult search(String query, List<String> attributes) {
    UserDirectorySearchResult result = new UserDirectorySearchResult();
    result.setLimited(false);
    try (LdapConnection conn = getConn()) {
        bind(conn);
        LdapConfig.Attribute 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);
        log.debug("Base DN: {}", getBaseDn());
        log.debug("Query: {}", searchQuery);
        log.debug("Attributes: {}", GsonUtil.build().toJson(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;
}
Also used : EntryCursor(org.apache.directory.api.ldap.model.cursor.EntryCursor) IOException(java.io.IOException) InternalServerError(io.kamax.mxisd.exception.InternalServerError) UserDirectorySearchResult(io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult) LdapConfig(io.kamax.mxisd.config.ldap.LdapConfig) Entry(org.apache.directory.api.ldap.model.entry.Entry) CursorException(org.apache.directory.api.ldap.model.cursor.CursorException) CursorLdapReferralException(org.apache.directory.api.ldap.model.cursor.CursorLdapReferralException) LdapException(org.apache.directory.api.ldap.model.exception.LdapException) UserDirectorySearchResult(io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult) LdapConnection(org.apache.directory.ldap.client.api.LdapConnection)

Example 5 with EntryCursor

use of org.apache.directory.api.ldap.model.cursor.EntryCursor in project Singularity by HubSpot.

the class SingularityLDAPDatastore method getUser.

@Override
public Optional<SingularityUser> getUser(String user) {
    if (configuration.isStripUserEmailDomain()) {
        user = user.split("@")[0];
    }
    if (ldapCache.isPresent()) {
        Optional<SingularityUser> cachedResult = ldapCache.get().getIfPresent(user);
        if (cachedResult != null) {
            return cachedResult;
        }
    }
    final Set<String> groups = new HashSet<>();
    try {
        final LdapConnection connection = connectionPool.getConnection();
        try {
            checkState(connection.isConnected(), "not connected");
            checkState(connection.isAuthenticated(), "not authenticated");
            connection.bind();
            final long startTime = System.currentTimeMillis();
            try {
                final EntryCursor userCursor = connection.search(configuration.getUserBaseDN(), String.format(configuration.getUserFilter(), user), SearchScope.ONELEVEL, configuration.getUserNameAttribute(), configuration.getUserEmailAttribute());
                if (!userCursor.next()) {
                    if (ldapCache.isPresent()) {
                        ldapCache.get().put(user, Optional.empty());
                    }
                    return Optional.empty();
                }
                final Entry userEntry = userCursor.get();
                // get group info
                final EntryCursor cursor = connection.search(configuration.getGroupBaseDN(), String.format(configuration.getGroupFilter(), user), configuration.getGroupSearchScope(), configuration.getGroupNameAttribute());
                while (cursor.next()) {
                    groups.add(cursor.get().get(configuration.getGroupNameAttribute()).getString());
                }
                Optional<SingularityUser> result = Optional.of(new SingularityUser(user, com.google.common.base.Optional.fromNullable(Strings.emptyToNull(userEntry.get(configuration.getUserNameAttribute()).getString())), com.google.common.base.Optional.fromNullable(Strings.emptyToNull(userEntry.get(configuration.getUserEmailAttribute()).getString())), groups));
                if (ldapCache.isPresent()) {
                    ldapCache.get().put(user, result);
                }
                return result;
            } finally {
                LOG.trace("Loaded {}'s user data in {}", user, JavaUtils.duration(startTime));
                connection.unBind();
            }
        } finally {
            connectionPool.releaseConnection(connection);
        }
    } catch (Exception e) {
        throw Throwables.propagate(e);
    }
}
Also used : EntryCursor(org.apache.directory.api.ldap.model.cursor.EntryCursor) Entry(org.apache.directory.api.ldap.model.entry.Entry) SingularityUser(com.hubspot.singularity.SingularityUser) IOException(java.io.IOException) LdapException(org.apache.directory.api.ldap.model.exception.LdapException) HashSet(java.util.HashSet) LdapConnection(org.apache.directory.ldap.client.api.LdapConnection)

Aggregations

EntryCursor (org.apache.directory.api.ldap.model.cursor.EntryCursor)19 LdapException (org.apache.directory.api.ldap.model.exception.LdapException)15 Entry (org.apache.directory.api.ldap.model.entry.Entry)14 IOException (java.io.IOException)11 CursorException (org.apache.directory.api.ldap.model.cursor.CursorException)10 Attribute (org.apache.directory.api.ldap.model.entry.Attribute)7 CursorLdapReferralException (org.apache.directory.api.ldap.model.cursor.CursorLdapReferralException)5 LdapConnection (org.apache.directory.ldap.client.api.LdapConnection)5 LdapNetworkConnection (org.apache.directory.ldap.client.api.LdapNetworkConnection)5 DefaultEntry (org.apache.directory.api.ldap.model.entry.DefaultEntry)4 DefaultAttribute (org.apache.directory.api.ldap.model.entry.DefaultAttribute)3 LdapAuthenticationException (org.apache.directory.api.ldap.model.exception.LdapAuthenticationException)3 LdapInvalidDnException (org.apache.directory.api.ldap.model.exception.LdapInvalidDnException)3 UncheckedTimeoutException (com.google.common.util.concurrent.UncheckedTimeoutException)2 InternalServerError (io.kamax.mxisd.exception.InternalServerError)2 SEPASecurityException (it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException)2 ArrayList (java.util.ArrayList)2 HashSet (java.util.HashSet)2 LdapInvalidAttributeValueException (org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException)2 SearchRequestImpl (org.apache.directory.api.ldap.model.message.SearchRequestImpl)2