blob: a5fcb41422dd74be216a2a58ce327ddfc566cdc0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.guacamole.auth.ldap.user;
import com.google.inject.Inject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.directory.ldap.client.api.LdapConnection;
import org.apache.directory.api.ldap.model.entry.Entry;
import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.guacamole.auth.ldap.conf.ConfigurationService;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleServerException;
import org.apache.guacamole.auth.ldap.conf.LDAPGuacamoleProperties;
import org.apache.guacamole.auth.ldap.ObjectQueryService;
import org.apache.guacamole.net.auth.User;
import org.apache.guacamole.net.auth.simple.SimpleUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Service for queries the users visible to a particular Guacamole user
* according to an LDAP directory.
*/
public class UserService {
/**
* Logger for this class.
*/
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
/**
* Service for retrieving LDAP server configuration information.
*/
@Inject
private ConfigurationService confService;
/**
* Service for executing LDAP queries.
*/
@Inject
private ObjectQueryService queryService;
/**
* Returns all Guacamole users accessible to the user currently bound under
* the given LDAP connection.
*
* @param ldapConnection
* The current connection to the LDAP server, associated with the
* current user.
*
* @return
* All users accessible to the user currently bound under the given
* LDAP connection, as a map of connection identifier to corresponding
* user object.
*
* @throws GuacamoleException
* If an error occurs preventing retrieval of users.
*/
public Map<String, User> getUsers(LdapConnection ldapConnection)
throws GuacamoleException {
// Retrieve all visible user objects
Collection<String> attributes = confService.getUsernameAttributes();
List<Entry> results = queryService.search(ldapConnection,
confService.getUserBaseDN(),
confService.getUserSearchFilter(),
attributes,
null);
// Convert retrieved users to map of identifier to Guacamole user object
return queryService.asMap(results, entry -> {
// Get username from record
try {
String username = queryService.getIdentifier(entry, attributes);
if (username == null) {
logger.warn("User \"{}\" is missing a username attribute "
+ "and will be ignored.", entry.getDn().toString());
return null;
}
return new SimpleUser(username);
}
catch (LdapInvalidAttributeValueException e) {
return null;
}
});
}
/**
* Returns a list of all DNs corresponding to the users having the given
* username. If multiple username attributes are defined, or if uniqueness
* is not enforced across the username attribute, it is possible that this
* will return multiple DNs.
*
* @param ldapConnection
* The connection to the LDAP server to use when querying user DNs.
*
* @param username
* The username of the user whose corresponding user account DNs are
* to be retrieved.
*
* @return
* A list of all DNs corresponding to the users having the given
* username. If no such DNs exist, this list will be empty.
*
* @throws GuacamoleException
* If an error occurs while querying the user DNs, or if the username
* attribute property cannot be parsed within guacamole.properties.
*/
public List<Dn> getUserDNs(LdapConnection ldapConnection,
String username) throws GuacamoleException {
// Retrieve user objects having a matching username
List<Entry> results = queryService.search(ldapConnection,
confService.getUserBaseDN(),
confService.getUserSearchFilter(),
confService.getUsernameAttributes(),
username);
// Build list of all DNs for retrieved users
List<Dn> userDNs = new ArrayList<>(results.size());
results.forEach(entry -> userDNs.add(entry.getDn()));
return userDNs;
}
/**
* Determines the DN which corresponds to the user having the given
* username. The DN will either be derived directly from the user base DN,
* or queried from the LDAP server, depending on how LDAP authentication
* has been configured.
*
* @param username
* The username of the user whose corresponding DN should be returned.
*
* @return
* The DN which corresponds to the user having the given username.
*
* @throws GuacamoleException
* If required properties are missing, and thus the user DN cannot be
* determined.
*/
public Dn deriveUserDN(String username)
throws GuacamoleException {
// Pull username attributes from properties
List<String> usernameAttributes = confService.getUsernameAttributes();
// We need exactly one base DN to derive the user DN
if (usernameAttributes.size() != 1) {
logger.warn(String.format("Cannot directly derive user DN when "
+ "multiple username attributes are specified. Please "
+ "define an LDAP search DN using the \"%s\" property "
+ "in your \"guacamole.properties\".",
LDAPGuacamoleProperties.LDAP_SEARCH_BIND_DN.getName()));
return null;
}
// Derive user DN from base DN
try {
return new Dn(usernameAttributes.get(0) + "=" + username
+ "," + confService.getUserBaseDN().toString());
}
catch (LdapInvalidDnException e) {
throw new GuacamoleServerException("Error trying to derive user DN.", e);
}
}
}