Merge 0.9.14-incubating changes back to master.

This commit is contained in:
Michael Jumper
2017-11-12 18:30:39 -08:00
6 changed files with 301 additions and 54 deletions

View File

@@ -251,6 +251,24 @@ public class ConfigurationService {
); );
} }
/**
* Returns the boolean value for whether the connection should
* follow referrals or not. By default, it will not.
*
* @return
* The boolean value of whether to follow referrals
* as configured in guacamole.properties.
*
* @throws GuacamoleException
* If guacamole.properties cannot be parsed.
*/
public boolean getFollowReferrals() throws GuacamoleException {
return environment.getProperty(
LDAPGuacamoleProperties.LDAP_FOLLOW_REFERRALS,
false
);
}
/** /**
* Returns a set of LDAPSearchConstraints to apply globally * Returns a set of LDAPSearchConstraints to apply globally
* to all LDAP searches. * to all LDAP searches.
@@ -272,6 +290,23 @@ public class ConfigurationService {
return constraints; return constraints;
} }
/**
* Returns the maximum number of referral hops to follow.
*
* @return
* The maximum number of referral hops to follow
* as configured in guacamole.properties.
*
* @throws GuacamoleException
* If guacamole.properties cannot be parsed.
*/
public int getMaxReferralHops() throws GuacamoleException {
return environment.getProperty(
LDAPGuacamoleProperties.LDAP_MAX_REFERRAL_HOPS,
5
);
}
/** /**
* Returns the search filter that should be used when querying the * Returns the search filter that should be used when querying the
* LDAP server for Guacamole users. If no filter is specified, * LDAP server for Guacamole users. If no filter is specified,
@@ -292,4 +327,21 @@ public class ConfigurationService {
); );
} }
/**
* Returns the maximum number of seconds to wait for LDAP operations.
*
* @return
* The maximum number of seconds to wait for LDAP operations
* as configured in guacamole.properties.
*
* @throws GuacamoleException
* If guacamole.properties cannot be parsed.
*/
public int getOperationTimeout() throws GuacamoleException {
return environment.getProperty(
LDAPGuacamoleProperties.LDAP_OPERATION_TIMEOUT,
30
);
}
} }

View File

@@ -21,12 +21,14 @@ package org.apache.guacamole.auth.ldap;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.novell.ldap.LDAPConnection; import com.novell.ldap.LDAPConnection;
import com.novell.ldap.LDAPConstraints;
import com.novell.ldap.LDAPException; import com.novell.ldap.LDAPException;
import com.novell.ldap.LDAPJSSESecureSocketFactory; import com.novell.ldap.LDAPJSSESecureSocketFactory;
import com.novell.ldap.LDAPJSSEStartTLSFactory; import com.novell.ldap.LDAPJSSEStartTLSFactory;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleUnsupportedException; import org.apache.guacamole.GuacamoleUnsupportedException;
import org.apache.guacamole.auth.ldap.ReferralAuthHandler;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -111,6 +113,27 @@ public class LDAPConnectionService {
// Obtain appropriately-configured LDAPConnection instance // Obtain appropriately-configured LDAPConnection instance
LDAPConnection ldapConnection = createLDAPConnection(); LDAPConnection ldapConnection = createLDAPConnection();
// Configure LDAP connection constraints
LDAPConstraints ldapConstraints = ldapConnection.getConstraints();
if (ldapConstraints == null)
ldapConstraints = new LDAPConstraints();
// Set whether or not we follow referrals
ldapConstraints.setReferralFollowing(confService.getFollowReferrals());
// Set referral authentication to use the provided credentials.
if (userDN != null && !userDN.isEmpty())
ldapConstraints.setReferralHandler(new ReferralAuthHandler(userDN, password));
// Set the maximum number of referrals we follow
ldapConstraints.setHopLimit(confService.getMaxReferralHops());
// Set timelimit to wait for LDAP operations, converting to ms
ldapConstraints.setTimeLimit(confService.getOperationTimeout() * 1000);
// Apply the constraints to the connection
ldapConnection.setConstraints(ldapConstraints);
try { try {
// Connect to LDAP server // Connect to LDAP server

View File

@@ -19,6 +19,7 @@
package org.apache.guacamole.auth.ldap; package org.apache.guacamole.auth.ldap;
import org.apache.guacamole.properties.BooleanGuacamoleProperty;
import org.apache.guacamole.properties.IntegerGuacamoleProperty; import org.apache.guacamole.properties.IntegerGuacamoleProperty;
import org.apache.guacamole.properties.StringGuacamoleProperty; import org.apache.guacamole.properties.StringGuacamoleProperty;
@@ -174,4 +175,34 @@ public class LDAPGuacamoleProperties {
}; };
/**
* Whether or not we should follow referrals.
*/
public static final BooleanGuacamoleProperty LDAP_FOLLOW_REFERRALS = new BooleanGuacamoleProperty() {
@Override
public String getName() { return "ldap-follow-referrals"; }
};
/**
* Maximum number of referral hops to follow.
*/
public static final IntegerGuacamoleProperty LDAP_MAX_REFERRAL_HOPS = new IntegerGuacamoleProperty() {
@Override
public String getName() { return "ldap-max-referral-hops"; }
};
/**
* Number of seconds to wait for LDAP operations to complete.
*/
public static final IntegerGuacamoleProperty LDAP_OPERATION_TIMEOUT = new IntegerGuacamoleProperty() {
@Override
public String getName() { return "ldap-operation-timeout"; }
};
} }

View File

@@ -0,0 +1,76 @@
/*
* 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;
import com.google.inject.Inject;
import com.novell.ldap.LDAPAuthHandler;
import com.novell.ldap.LDAPAuthProvider;
import com.novell.ldap.LDAPConnection;
import java.io.UnsupportedEncodingException;
import org.apache.guacamole.GuacamoleException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class that implements the necessary authentication handling
* for following referrals in LDAP connections.
*/
public class ReferralAuthHandler implements LDAPAuthHandler {
/**
* Logger for this class.
*/
private final Logger logger = LoggerFactory.getLogger(ReferralAuthHandler.class);
/**
* The LDAPAuthProvider object that will be set and returned to the referral handler.
*/
private final LDAPAuthProvider ldapAuth;
/**
* Creates a ReferralAuthHandler object to handle authentication when
* following referrals in a LDAP connection, using the provided dn and
* password.
*/
public ReferralAuthHandler(String dn, String password) {
byte[] passwordBytes;
try {
// Convert password into corresponding byte array
if (password != null)
passwordBytes = password.getBytes("UTF-8");
else
passwordBytes = null;
}
catch (UnsupportedEncodingException e) {
logger.error("Unexpected lack of support for UTF-8: {}", e.getMessage());
logger.debug("Support for UTF-8 (as required by Java spec) not found.", e);
throw new UnsupportedOperationException("Unexpected lack of UTF-8 support.", e);
}
ldapAuth = new LDAPAuthProvider(dn, passwordBytes);
}
@Override
public LDAPAuthProvider getAuthProvider(String host, int port) {
return ldapAuth;
}
}

View File

@@ -24,6 +24,7 @@ import com.novell.ldap.LDAPAttribute;
import com.novell.ldap.LDAPConnection; import com.novell.ldap.LDAPConnection;
import com.novell.ldap.LDAPEntry; import com.novell.ldap.LDAPEntry;
import com.novell.ldap.LDAPException; import com.novell.ldap.LDAPException;
import com.novell.ldap.LDAPReferralException;
import com.novell.ldap.LDAPSearchResults; import com.novell.ldap.LDAPSearchResults;
import java.util.Collections; import java.util.Collections;
import java.util.Enumeration; import java.util.Enumeration;
@@ -129,6 +130,8 @@ public class ConnectionService {
Map<String, Connection> connections = new HashMap<String, Connection>(); Map<String, Connection> connections = new HashMap<String, Connection>();
while (results.hasMore()) { while (results.hasMore()) {
try {
LDAPEntry entry = results.next(); LDAPEntry entry = results.next();
// Get common name (CN) // Get common name (CN)
@@ -188,6 +191,21 @@ public class ConnectionService {
} }
// Deal with issues following LDAP referrals
catch (LDAPReferralException e) {
if (confService.getFollowReferrals()) {
logger.error("Could not follow referral: {}", e.getFailedReferral());
logger.debug("Error encountered trying to follow referral.", e);
throw new GuacamoleServerException("Could not follow LDAP referral.", e);
}
else {
logger.warn("Given a referral, but referrals are disabled. Error was: {}", e.getMessage());
logger.debug("Got a referral, but configured to not follow them.", e);
}
}
}
// Return map of all connections // Return map of all connections
return connections; return connections;
@@ -251,9 +269,23 @@ public class ConnectionService {
// The guacConfig group uses the seeAlso attribute to refer // The guacConfig group uses the seeAlso attribute to refer
// to these other groups // to these other groups
while (userRoleGroupResults.hasMore()) { while (userRoleGroupResults.hasMore()) {
try {
LDAPEntry entry = userRoleGroupResults.next(); LDAPEntry entry = userRoleGroupResults.next();
connectionSearchFilter.append("(seeAlso=").append(escapingService.escapeLDAPSearchFilter(entry.getDN())).append(")"); connectionSearchFilter.append("(seeAlso=").append(escapingService.escapeLDAPSearchFilter(entry.getDN())).append(")");
} }
catch (LDAPReferralException e) {
if (confService.getFollowReferrals()) {
logger.error("Could not follow referral: {}", e.getFailedReferral());
logger.debug("Error encountered trying to follow referral.", e);
throw new GuacamoleServerException("Could not follow LDAP referral.", e);
}
else {
logger.warn("Given a referral, but referrals are disabled. Error was: {}", e.getMessage());
logger.debug("Got a referral, but configured to not follow them.", e);
}
}
}
} }
// Complete the search filter. // Complete the search filter.

View File

@@ -24,6 +24,7 @@ import com.novell.ldap.LDAPAttribute;
import com.novell.ldap.LDAPConnection; import com.novell.ldap.LDAPConnection;
import com.novell.ldap.LDAPEntry; import com.novell.ldap.LDAPEntry;
import com.novell.ldap.LDAPException; import com.novell.ldap.LDAPException;
import com.novell.ldap.LDAPReferralException;
import com.novell.ldap.LDAPSearchResults; import com.novell.ldap.LDAPSearchResults;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@@ -107,6 +108,8 @@ public class UserService {
// Read all visible users // Read all visible users
while (results.hasMore()) { while (results.hasMore()) {
try {
LDAPEntry entry = results.next(); LDAPEntry entry = results.next();
// Get username from record // Get username from record
@@ -123,6 +126,21 @@ public class UserService {
} }
// Deal with errors trying to follow referrals
catch (LDAPReferralException e) {
if (confService.getFollowReferrals()) {
logger.error("Could not follow referral: {}", e.getFailedReferral());
logger.debug("Error encountered trying to follow referral.", e);
throw new GuacamoleServerException("Could not follow LDAP referral.", e);
}
else {
logger.warn("Given a referral, but referrals are disabled. Error was: {}", e.getMessage());
logger.debug("Got a referral, but configured to not follow them.", e);
}
}
}
} }
catch (LDAPException e) { catch (LDAPException e) {
throw new GuacamoleServerException("Error while querying users.", e); throw new GuacamoleServerException("Error while querying users.", e);
@@ -267,10 +285,25 @@ public class UserService {
// Add all DNs for found users // Add all DNs for found users
while (results.hasMore()) { while (results.hasMore()) {
try {
LDAPEntry entry = results.next(); LDAPEntry entry = results.next();
userDNs.add(entry.getDN()); userDNs.add(entry.getDN());
} }
// Deal with errors following referrals
catch (LDAPReferralException e) {
if (confService.getFollowReferrals()) {
logger.error("Error trying to follow a referral: {}", e.getFailedReferral());
logger.debug("Encountered an error trying to follow a referral.", e);
throw new GuacamoleServerException("Failed while trying to follow referrals.", e);
}
else {
logger.warn("Given a referral, not following it. Error was: {}", e.getMessage());
logger.debug("Given a referral, but configured to not follow them.", e);
}
}
}
// Return all discovered DNs (if any) // Return all discovered DNs (if any)
return userDNs; return userDNs;