GUACAMOLE-957: Support reading multiple LDAP server configurations from "ldap-servers.yml".

This commit is contained in:
Michael Jumper
2021-10-20 23:35:13 -07:00
parent 278bfa17ae
commit 49a4a6c7a0
8 changed files with 383 additions and 2 deletions

View File

@@ -60,6 +60,16 @@
<artifactId>guice</artifactId>
</dependency>
<!-- Jackson and YAML support -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -196,7 +196,7 @@ public class AuthenticationProviderService {
String password) throws GuacamoleException {
// Get relevant LDAP configurations for user
Collection<LDAPConfiguration> configs = confService.getLDAPConfigurations(username);
Collection<? extends LDAPConfiguration> configs = confService.getLDAPConfigurations(username);
if (configs.isEmpty()) {
logger.info("User \"{}\" does not map to any defined LDAP configurations.", username);
return null;

View File

@@ -19,17 +19,41 @@
package org.apache.guacamole.auth.ldap.conf;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.google.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleServerException;
import org.apache.guacamole.environment.Environment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Service for retrieving configuration information regarding LDAP servers.
*/
public class ConfigurationService {
/**
* Logger for this class.
*/
private static final Logger logger = LoggerFactory.getLogger(ConfigurationService.class);
/**
* ObjectMapper for deserializing YAML.
*/
private final ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
/**
* The name of the file within GUACAMOLE_HOME that defines each available
* LDAP server (if not using guacamole.properties).
*/
private static final String LDAP_SERVERS_YML = "ldap-servers.yml";
/**
* The Guacamole server environment.
*/
@@ -54,8 +78,25 @@ public class ConfigurationService {
* If the configuration information of the LDAP servers related to the
* user having the given username cannot be retrieved due to an error.
*/
public Collection<LDAPConfiguration> getLDAPConfigurations(String username) throws GuacamoleException {
public Collection<? extends LDAPConfiguration> getLDAPConfigurations(String username) throws GuacamoleException {
// Read configuration from YAML, if available
File ldapServers = new File(environment.getGuacamoleHome(), LDAP_SERVERS_YML);
if (ldapServers.exists()) {
try {
logger.debug("Reading LDAP configuration from \"{}\"...", ldapServers);
return mapper.readValue(ldapServers, new TypeReference<Collection<JacksonLDAPConfiguration>>() {});
}
catch (IOException e) {
logger.error("\"{}\" could not be read/parsed: {}", ldapServers, e.getMessage());
throw new GuacamoleServerException("Cannot read LDAP configuration from \"" + ldapServers + "\"", e);
}
}
// Use guacamole.properties if not using YAML
logger.debug("Reading LDAP configuration from guacamole.properties...");
return Collections.singletonList(new EnvironmentLDAPConfiguration(environment));
}
}

View File

@@ -0,0 +1,315 @@
/*
* 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.conf;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Collections;
import java.util.List;
import org.apache.directory.api.ldap.model.filter.ExprNode;
import org.apache.directory.api.ldap.model.filter.PresenceNode;
import org.apache.directory.api.ldap.model.message.AliasDerefMode;
import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleServerException;
/**
* LDAPConfiguration implementation that is annotated for deserialization by
* Jackson.
*/
public class JacksonLDAPConfiguration implements LDAPConfiguration {
/**
* The raw YAML value of {@link LDAPGuacamoleProperties#LDAP_HOSTNAME}. If
* not set within the YAML, this will be null.
*/
@JsonProperty("hostname")
private String hostname;
/**
* The raw YAML value of {@link LDAPGuacamoleProperties#LDAP_PORT}. If not
* set within the YAML, this will be null.
*/
@JsonProperty("port")
private Integer port;
/**
* The raw YAML value of {@link LDAPGuacamoleProperties#LDAP_USERNAME_ATTRIBUTES}.
* If not set within the YAML, this will be null.
*/
@JsonProperty("username-attribute")
private List<String> usernameAttributes;
/**
* The raw YAML value of {@link LDAPGuacamoleProperties#LDAP_USER_BASE_DN}.
* If not set within the YAML, this will be null.
*/
@JsonProperty("user-base-dn")
private String userBaseDn;
/**
* The raw YAML value of {@link LDAPGuacamoleProperties#LDAP_CONFIG_BASE_DN}.
* If not set within the YAML, this will be null.
*/
@JsonProperty("config-base-dn")
private String configBaseDn;
/**
* The raw YAML value of {@link LDAPGuacamoleProperties#LDAP_GROUP_BASE_DN}.
* If not set within the YAML, this will be null.
*/
@JsonProperty("group-base-dn")
private String groupBaseDn;
/**
* The raw YAML value of {@link LDAPGuacamoleProperties#LDAP_GROUP_NAME_ATTRIBUTES}.
* If not set within the YAML, this will be null.
*/
@JsonProperty("group-name-attribute")
private List<String> groupNameAttributes;
/**
* The raw YAML value of {@link LDAPGuacamoleProperties#LDAP_SEARCH_BIND_DN}.
* If not set within the YAML, this will be null.
*/
@JsonProperty("search-bind-dn")
private String searchBindDn;
/**
* The raw YAML value of {@link LDAPGuacamoleProperties#LDAP_SEARCH_BIND_PASSWORD}.
* If not set within the YAML, this will be null.
*/
@JsonProperty("search-bind-password")
private String searchBindPassword;
/**
* The raw YAML value of {@link LDAPGuacamoleProperties#LDAP_ENCRYPTION_METHOD}.
* If not set within the YAML, this will be null.
*/
@JsonProperty("encryption-method")
private String encryptionMethod;
/**
* The raw YAML value of {@link LDAPGuacamoleProperties#LDAP_MAX_SEARCH_RESULTS}.
* If not set within the YAML, this will be null.
*/
@JsonProperty("max-search-results")
private Integer maxSearchResults;
/**
* The raw YAML value of {@link LDAPGuacamoleProperties#LDAP_DEREFERENCE_ALIASES}.
* If not set within the YAML, this will be null.
*/
@JsonProperty("dereference-aliases")
private String dereferenceAliases;
/**
* The raw YAML value of {@link LDAPGuacamoleProperties#LDAP_FOLLOW_REFERRALS}.
* If not set within the YAML, this will be null.
*/
@JsonProperty("follow-referrals")
private Boolean followReferrals;
/**
* The raw YAML value of {@link LDAPGuacamoleProperties#LDAP_MAX_REFERRAL_HOPS}.
* If not set within the YAML, this will be null.
*/
@JsonProperty("max-referral-hops")
private Integer maxReferralHops;
/**
* The raw YAML value of {@link LDAPGuacamoleProperties#LDAP_USER_SEARCH_FILTER}.
* If not set within the YAML, this will be null.
*/
@JsonProperty("user-search-filter")
private String userSearchFilter;
/**
* The raw YAML value of {@link LDAPGuacamoleProperties#LDAP_GROUP_SEARCH_FILTER}.
* If not set within the YAML, this will be null.
*/
@JsonProperty("group-search-filter")
private String groupSearchFilter;
/**
* The raw YAML value of {@link LDAPGuacamoleProperties#LDAP_OPERATION_TIMEOUT}.
* If not set within the YAML, this will be null.
*/
@JsonProperty("operation-timeout")
private Integer operationTimeout;
/**
* The raw YAML value of {@link LDAPGuacamoleProperties#LDAP_USER_ATTRIBUTES}.
* If not set within the YAML, this will be null.
*/
@JsonProperty("user-attributes")
private List<String> userAttributes;
/**
* The raw YAML value of {@link LDAPGuacamoleProperties#LDAP_MEMBER_ATTRIBUTE}.
* If not set within the YAML, this will be null.
*/
@JsonProperty("member-attribute")
private String memberAttribute;
/**
* The raw YAML value of {@link LDAPGuacamoleProperties#LDAP_MEMBER_ATTRIBUTE_TYPE}.
* If not set within the YAML, this will be null.
*/
@JsonProperty("member-attribute-type")
private String memberAttributeType;
@Override
public String getServerHostname() {
return hostname != null ? hostname : "localhost";
}
@Override
public int getServerPort() throws GuacamoleException {
return port != null ? port : getEncryptionMethod().DEFAULT_PORT;
}
@Override
public List<String> getUsernameAttributes() {
return usernameAttributes != null ? usernameAttributes : Collections.singletonList("uid");
}
@Override
public Dn getUserBaseDN() throws GuacamoleException {
Dn parsedDn = LDAPGuacamoleProperties.LDAP_USER_BASE_DN.parseValue(userBaseDn);
if (parsedDn == null)
throw new GuacamoleServerException("The \"user-base-dn\" property is required for all LDAP servers.");
return parsedDn;
}
@Override
public Dn getConfigurationBaseDN() throws GuacamoleException {
return LDAPGuacamoleProperties.LDAP_CONFIG_BASE_DN.parseValue(configBaseDn);
}
@Override
public List<String> getGroupNameAttributes() throws GuacamoleException {
return groupNameAttributes != null ? groupNameAttributes : Collections.singletonList("cn");
}
@Override
public Dn getGroupBaseDN() throws GuacamoleException {
return LDAPGuacamoleProperties.LDAP_GROUP_BASE_DN.parseValue(groupBaseDn);
}
@Override
public String getSearchBindDN() throws GuacamoleException {
return searchBindDn;
}
@Override
public String getSearchBindPassword() throws GuacamoleException {
return searchBindPassword;
}
@Override
public EncryptionMethod getEncryptionMethod() throws GuacamoleException {
EncryptionMethod parsedMethod = LDAPGuacamoleProperties.LDAP_ENCRYPTION_METHOD.parseValue(encryptionMethod);
if (parsedMethod == null)
return EncryptionMethod.NONE;
return parsedMethod;
}
@Override
public int getMaxResults() throws GuacamoleException {
return maxSearchResults != null ? maxSearchResults : 1000;
}
@Override
public AliasDerefMode getDereferenceAliases() throws GuacamoleException {
AliasDerefMode parsedMode = LDAPGuacamoleProperties.LDAP_DEREFERENCE_ALIASES.parseValue(dereferenceAliases);
if (parsedMode == null)
return AliasDerefMode.NEVER_DEREF_ALIASES;
return parsedMode;
}
@Override
public boolean getFollowReferrals() throws GuacamoleException {
return followReferrals != null ? followReferrals : false;
}
@Override
public int getMaxReferralHops() throws GuacamoleException {
return maxReferralHops != null ? maxReferralHops : 5;
}
@Override
public ExprNode getUserSearchFilter() throws GuacamoleException {
ExprNode parsedFilter = LDAPGuacamoleProperties.LDAP_USER_SEARCH_FILTER.parseValue(userSearchFilter);
if (parsedFilter == null)
return new PresenceNode("objectClass");
return parsedFilter;
}
@Override
public ExprNode getGroupSearchFilter() throws GuacamoleException {
ExprNode parsedFilter = LDAPGuacamoleProperties.LDAP_GROUP_SEARCH_FILTER.parseValue(groupSearchFilter);
if (parsedFilter == null)
return new PresenceNode("objectClass");
return parsedFilter;
}
@Override
public int getOperationTimeout() throws GuacamoleException {
return operationTimeout != null ? operationTimeout : 30;
}
@Override
public List<String> getAttributes() throws GuacamoleException {
return userAttributes != null ? userAttributes : Collections.<String>emptyList();
}
@Override
public String getMemberAttribute() throws GuacamoleException {
return memberAttribute != null ? memberAttribute : "member";
}
@Override
public MemberAttributeType getMemberAttributeType()
throws GuacamoleException {
MemberAttributeType parsedType = LDAPGuacamoleProperties.LDAP_MEMBER_ATTRIBUTE_TYPE.parseValue(memberAttributeType);
if (parsedType == null)
return MemberAttributeType.DN;
return parsedType;
}
}