use of org.graylog2.shared.security.ldap.LdapEntry in project graylog2-server by Graylog2.
the class LdapUserAuthenticator method updateFromLdap.
private void updateFromLdap(User user, LdapEntry userEntry, LdapSettings ldapSettings, String username) {
final String displayNameAttribute = ldapSettings.getDisplayNameAttribute();
final String fullName = firstNonNull(userEntry.get(displayNameAttribute), username);
user.setName(username);
user.setFullName(fullName);
user.setExternal(true);
if (user.getTimeZone() == null) {
user.setTimeZone(rootTimeZone);
}
final String email = userEntry.getEmail();
if (isNullOrEmpty(email)) {
LOG.debug("No email address found for user {} in LDAP. Using {}@localhost", username, username);
user.setEmail(username + "@localhost");
} else {
user.setEmail(email);
}
// TODO This is a crude hack until we have a proper way to distinguish LDAP users from normal users
if (isNullOrEmpty(user.getHashedPassword())) {
((UserImpl) user).setHashedPassword("User synced from LDAP.");
}
// map ldap groups to user roles, if the mapping is present
final Set<String> translatedRoleIds = Sets.newHashSet(Sets.union(Sets.newHashSet(ldapSettings.getDefaultGroupId()), ldapSettings.getAdditionalDefaultGroupIds()));
if (!userEntry.getGroups().isEmpty()) {
// ldap search returned groups, these always override the ones set on the user
try {
final Map<String, Role> roleNameToRole = roleService.loadAllLowercaseNameMap();
for (String ldapGroupName : userEntry.getGroups()) {
final String roleName = ldapSettings.getGroupMapping().get(ldapGroupName);
if (roleName == null) {
LOG.debug("User {}: No group mapping for ldap group <{}>", username, ldapGroupName);
continue;
}
final Role role = roleNameToRole.get(roleName.toLowerCase(Locale.ENGLISH));
if (role != null) {
LOG.debug("User {}: Mapping ldap group <{}> to role <{}>", username, ldapGroupName, role.getName());
translatedRoleIds.add(role.getId());
} else {
LOG.warn("User {}: No role found for ldap group <{}>", username, ldapGroupName);
}
}
} catch (NotFoundException e) {
LOG.error("Unable to load user roles", e);
}
} else if (ldapSettings.getGroupMapping().isEmpty() || ldapSettings.getGroupSearchBase().isEmpty() || ldapSettings.getGroupSearchPattern().isEmpty() || ldapSettings.getGroupIdAttribute().isEmpty()) {
// no group mapping or configuration set, we'll leave the previously set groups alone on sync
// when first creating the user these will be empty
translatedRoleIds.addAll(user.getRoleIds());
}
user.setRoleIds(translatedRoleIds);
// preserve the raw permissions (the ones without the synthetic self-edit permissions or the "*" admin one)
user.setPermissions(user.getPermissions());
}
use of org.graylog2.shared.security.ldap.LdapEntry in project graylog2-server by Graylog2.
the class LdapResource method testLdapConfiguration.
@POST
@Timed
@RequiresPermissions(RestPermissions.LDAP_EDIT)
@ApiOperation("Test LDAP Configuration")
@Path("/test")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@NoAuditEvent("only used to test LDAP configuration")
public LdapTestConfigResponse testLdapConfiguration(@ApiParam(name = "Configuration to test", required = true) @Valid @NotNull LdapTestConfigRequest request) {
final LdapConnectionConfig config = new LdapConnectionConfig();
final URI ldapUri = request.ldapUri();
config.setLdapHost(ldapUri.getHost());
config.setLdapPort(ldapUri.getPort());
config.setUseSsl(ldapUri.getScheme().startsWith("ldaps"));
config.setUseTls(request.useStartTls());
if (request.trustAllCertificates()) {
config.setTrustManagers(new TrustAllX509TrustManager());
}
if (!isNullOrEmpty(request.systemUsername()) && !isNullOrEmpty(request.systemPassword())) {
config.setName(request.systemUsername());
config.setCredentials(request.systemPassword());
}
LdapNetworkConnection connection = null;
try {
try {
connection = ldapConnector.connect(config);
} catch (LdapException e) {
return LdapTestConfigResponse.create(false, false, false, Collections.<String, String>emptyMap(), Collections.<String>emptySet(), e.getMessage());
}
if (null == connection) {
return LdapTestConfigResponse.create(false, false, false, Collections.<String, String>emptyMap(), Collections.<String>emptySet(), "Could not connect to LDAP server");
}
boolean connected = connection.isConnected();
boolean systemAuthenticated = connection.isAuthenticated();
// the web interface allows testing the connection only, in that case we can bail out early.
if (request.testConnectOnly()) {
return LdapTestConfigResponse.create(connected, systemAuthenticated, false, Collections.<String, String>emptyMap(), Collections.<String>emptySet());
}
String userPrincipalName = null;
boolean loginAuthenticated = false;
Map<String, String> entryMap = Collections.emptyMap();
String exception = null;
Set<String> groups = Collections.emptySet();
try {
final LdapEntry entry = ldapConnector.search(connection, request.searchBase(), request.searchPattern(), "*", request.principal(), request.activeDirectory(), request.groupSearchBase(), request.groupIdAttribute(), request.groupSearchPattern());
if (entry != null) {
userPrincipalName = entry.getBindPrincipal();
entryMap = entry.getAttributes();
groups = entry.getGroups();
}
} catch (CursorException | LdapException e) {
exception = e.getMessage();
}
try {
loginAuthenticated = ldapConnector.authenticate(connection, userPrincipalName, request.password());
} catch (Exception e) {
exception = e.getMessage();
}
return LdapTestConfigResponse.create(connected, systemAuthenticated, loginAuthenticated, entryMap, groups, exception);
} finally {
if (connection != null) {
try {
connection.close();
} catch (IOException e) {
LOG.warn("Unable to close LDAP connection.", e);
}
}
}
}
use of org.graylog2.shared.security.ldap.LdapEntry 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;
}
}
use of org.graylog2.shared.security.ldap.LdapEntry 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;
}
use of org.graylog2.shared.security.ldap.LdapEntry in project graylog2-server by Graylog2.
the class LdapConnectorTest method testAllGroupClassesLookup.
@Test
public void testAllGroupClassesLookup() throws Exception {
final LdapEntry entry = connector.search(connection, "ou=users,dc=example,dc=com", "(&(objectClass=posixAccount)(uid={0}))", "cn", "john", false, "ou=groups,dc=example,dc=com", "cn", "(|(objectClass=posixGroup)(objectClass=groupOfNames)(objectclass=groupOfUniqueNames))");
assertThat(entry).isNotNull();
assertThat(entry.getDn()).isNotNull().isEqualTo("cn=John Doe,ou=users,dc=example,dc=com");
assertThat(entry.getGroups()).hasSize(4).contains("Developers", "QA", "Engineers", "Whitespace Engineers");
}
Aggregations