mirror of
				https://github.com/gyurix1968/guacamole-client.git
				synced 2025-10-31 00:53:21 +00:00 
			
		
		
		
	Merge pull request #251 from glyptodon/ldap-users
GUAC-586: Support listing users within LDAP authentication
This commit is contained in:
		| @@ -102,6 +102,18 @@ | |||||||
|             <version>4.3</version> |             <version>4.3</version> | ||||||
|         </dependency> |         </dependency> | ||||||
|  |  | ||||||
|  |         <!-- Guice --> | ||||||
|  |         <dependency> | ||||||
|  |             <groupId>com.google.inject</groupId> | ||||||
|  |             <artifactId>guice</artifactId> | ||||||
|  |             <version>3.0</version> | ||||||
|  |         </dependency> | ||||||
|  |         <dependency> | ||||||
|  |             <groupId>com.google.inject.extensions</groupId> | ||||||
|  |             <artifactId>guice-multibindings</artifactId> | ||||||
|  |             <version>3.0</version> | ||||||
|  |         </dependency> | ||||||
|  |  | ||||||
|     </dependencies> |     </dependencies> | ||||||
|  |  | ||||||
| </project> | </project> | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| /* | /* | ||||||
|  * Copyright (C) 2013 Glyptodon LLC |  * Copyright (C) 2015 Glyptodon LLC | ||||||
|  * |  * | ||||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy |  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  * of this software and associated documentation files (the "Software"), to deal |  * of this software and associated documentation files (the "Software"), to deal | ||||||
| @@ -23,25 +23,15 @@ | |||||||
| package net.sourceforge.guacamole.net.auth.ldap; | package net.sourceforge.guacamole.net.auth.ldap; | ||||||
|  |  | ||||||
|  |  | ||||||
| import com.novell.ldap.LDAPAttribute; | import org.glyptodon.guacamole.auth.ldap.AuthenticationProviderService; | ||||||
| import com.novell.ldap.LDAPConnection; | import org.glyptodon.guacamole.auth.ldap.LDAPAuthenticationProviderModule; | ||||||
| import com.novell.ldap.LDAPEntry; | import com.google.inject.Guice; | ||||||
| import com.novell.ldap.LDAPException; | import com.google.inject.Injector; | ||||||
| import com.novell.ldap.LDAPSearchResults; |  | ||||||
| import java.io.UnsupportedEncodingException; |  | ||||||
| import java.util.Enumeration; |  | ||||||
| import java.util.Map; |  | ||||||
| import java.util.TreeMap; |  | ||||||
| import org.glyptodon.guacamole.GuacamoleException; | import org.glyptodon.guacamole.GuacamoleException; | ||||||
|  | import org.glyptodon.guacamole.net.auth.AuthenticatedUser; | ||||||
|  | import org.glyptodon.guacamole.net.auth.AuthenticationProvider; | ||||||
| import org.glyptodon.guacamole.net.auth.Credentials; | import org.glyptodon.guacamole.net.auth.Credentials; | ||||||
| import net.sourceforge.guacamole.net.auth.ldap.properties.LDAPGuacamoleProperties; | import org.glyptodon.guacamole.net.auth.UserContext; | ||||||
| import org.glyptodon.guacamole.GuacamoleServerException; |  | ||||||
| import org.glyptodon.guacamole.environment.Environment; |  | ||||||
| import org.glyptodon.guacamole.environment.LocalEnvironment; |  | ||||||
| import org.glyptodon.guacamole.net.auth.simple.SimpleAuthenticationProvider; |  | ||||||
| import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; |  | ||||||
| import org.slf4j.Logger; |  | ||||||
| import org.slf4j.LoggerFactory; |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Allows users to be authenticated against an LDAP server. Each user may have |  * Allows users to be authenticated against an LDAP server. Each user may have | ||||||
| @@ -50,17 +40,18 @@ import org.slf4j.LoggerFactory; | |||||||
|  * |  * | ||||||
|  * @author Michael Jumper |  * @author Michael Jumper | ||||||
|  */ |  */ | ||||||
| public class LDAPAuthenticationProvider extends SimpleAuthenticationProvider { | public class LDAPAuthenticationProvider implements AuthenticationProvider { | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Logger for this class. |      * The identifier reserved for the root connection group. | ||||||
|      */ |      */ | ||||||
|     private Logger logger = LoggerFactory.getLogger(LDAPAuthenticationProvider.class); |     public static final String ROOT_CONNECTION_GROUP = "ROOT"; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Guacamole server environment. |      * Injector which will manage the object graph of this authentication | ||||||
|  |      * provider. | ||||||
|      */ |      */ | ||||||
|     private final Environment environment; |     private final Injector injector; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Creates a new LDAPAuthenticationProvider that authenticates users |      * Creates a new LDAPAuthenticationProvider that authenticates users | ||||||
| @@ -71,7 +62,12 @@ public class LDAPAuthenticationProvider extends SimpleAuthenticationProvider { | |||||||
|      *     a property. |      *     a property. | ||||||
|      */ |      */ | ||||||
|     public LDAPAuthenticationProvider() throws GuacamoleException { |     public LDAPAuthenticationProvider() throws GuacamoleException { | ||||||
|         environment = new LocalEnvironment(); |  | ||||||
|  |         // Set up Guice injector. | ||||||
|  |         injector = Guice.createInjector( | ||||||
|  |             new LDAPAuthenticationProviderModule(this) | ||||||
|  |         ); | ||||||
|  |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
| @@ -79,219 +75,33 @@ public class LDAPAuthenticationProvider extends SimpleAuthenticationProvider { | |||||||
|         return "ldap"; |         return "ldap"; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Courtesy of OWASP: https://www.owasp.org/index.php/Preventing_LDAP_Injection_in_Java |     @Override | ||||||
|     private static String escapeLDAPSearchFilter(String filter) { |     public AuthenticatedUser authenticateUser(Credentials credentials) throws GuacamoleException { | ||||||
|         StringBuilder sb = new StringBuilder(); |  | ||||||
|         for (int i = 0; i < filter.length(); i++) { |         AuthenticationProviderService authProviderService = injector.getInstance(AuthenticationProviderService.class); | ||||||
|             char curChar = filter.charAt(i); |         return authProviderService.authenticateUser(credentials); | ||||||
|             switch (curChar) { |  | ||||||
|                 case '\\': |  | ||||||
|                     sb.append("\\5c"); |  | ||||||
|                     break; |  | ||||||
|                 case '*': |  | ||||||
|                     sb.append("\\2a"); |  | ||||||
|                     break; |  | ||||||
|                 case '(': |  | ||||||
|                     sb.append("\\28"); |  | ||||||
|                     break; |  | ||||||
|                 case ')': |  | ||||||
|                     sb.append("\\29"); |  | ||||||
|                     break; |  | ||||||
|                 case '\u0000': |  | ||||||
|                     sb.append("\\00"); |  | ||||||
|                     break; |  | ||||||
|                 default: |  | ||||||
|                     sb.append(curChar); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         return sb.toString(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Courtesy of OWASP: https://www.owasp.org/index.php/Preventing_LDAP_Injection_in_Java |  | ||||||
|     private static String escapeDN(String name) { |  | ||||||
|        StringBuilder sb = new StringBuilder(); |  | ||||||
|        if ((name.length() > 0) && ((name.charAt(0) == ' ') || (name.charAt(0) == '#'))) { |  | ||||||
|            sb.append('\\'); // add the leading backslash if needed |  | ||||||
|        } |  | ||||||
|        for (int i = 0; i < name.length(); i++) { |  | ||||||
|            char curChar = name.charAt(i); |  | ||||||
|            switch (curChar) { |  | ||||||
|                case '\\': |  | ||||||
|                    sb.append("\\\\"); |  | ||||||
|                    break; |  | ||||||
|                case ',': |  | ||||||
|                    sb.append("\\,"); |  | ||||||
|                    break; |  | ||||||
|                case '+': |  | ||||||
|                    sb.append("\\+"); |  | ||||||
|                    break; |  | ||||||
|                case '"': |  | ||||||
|                    sb.append("\\\""); |  | ||||||
|                    break; |  | ||||||
|                case '<': |  | ||||||
|                    sb.append("\\<"); |  | ||||||
|                    break; |  | ||||||
|                case '>': |  | ||||||
|                    sb.append("\\>"); |  | ||||||
|                    break; |  | ||||||
|                case ';': |  | ||||||
|                    sb.append("\\;"); |  | ||||||
|                    break; |  | ||||||
|                default: |  | ||||||
|                    sb.append(curChar); |  | ||||||
|            } |  | ||||||
|        } |  | ||||||
|        if ((name.length() > 1) && (name.charAt(name.length() - 1) == ' ')) { |  | ||||||
|            sb.insert(sb.length() - 1, '\\'); // add the trailing backslash if needed |  | ||||||
|        } |  | ||||||
|        return sb.toString(); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public Map<String, GuacamoleConfiguration> getAuthorizedConfigurations(Credentials credentials) throws GuacamoleException { |     public AuthenticatedUser updateAuthenticatedUser(AuthenticatedUser authenticatedUser, | ||||||
|  |             Credentials credentials) throws GuacamoleException { | ||||||
|         // Require username |         return authenticatedUser; | ||||||
|         if (credentials.getUsername() == null) { |  | ||||||
|             logger.debug("Anonymous bind is not currently allowed by the LDAP authentication provider."); |  | ||||||
|             return null; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|         // Require password, and do not allow anonymous binding |     @Override | ||||||
|         if (credentials.getPassword() == null |     public UserContext getUserContext(AuthenticatedUser authenticatedUser) | ||||||
|                 || credentials.getPassword().length() == 0) { |             throws GuacamoleException { | ||||||
|             logger.debug("Anonymous bind is not currently allowed by the LDAP authentication provider."); |  | ||||||
|             return null; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Connect to LDAP server |         AuthenticationProviderService authProviderService = injector.getInstance(AuthenticationProviderService.class); | ||||||
|         LDAPConnection ldapConnection; |         return authProviderService.getUserContext(authenticatedUser); | ||||||
|         try { |  | ||||||
|  |  | ||||||
|             ldapConnection = new LDAPConnection(); |  | ||||||
|             ldapConnection.connect( |  | ||||||
|                     environment.getRequiredProperty(LDAPGuacamoleProperties.LDAP_HOSTNAME), |  | ||||||
|                     environment.getRequiredProperty(LDAPGuacamoleProperties.LDAP_PORT) |  | ||||||
|             ); |  | ||||||
|  |  | ||||||
|     } |     } | ||||||
|         catch (LDAPException e) { |  | ||||||
|             throw new GuacamoleServerException("Unable to connect to LDAP server.", e); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Get username attribute |  | ||||||
|         String username_attribute = environment.getRequiredProperty( |  | ||||||
|             LDAPGuacamoleProperties.LDAP_USERNAME_ATTRIBUTE |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         // Get user base DN |  | ||||||
|         String user_base_dn = environment.getRequiredProperty( |  | ||||||
|                 LDAPGuacamoleProperties.LDAP_USER_BASE_DN |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         // Construct user DN |  | ||||||
|         String user_dn = |  | ||||||
|             escapeDN(username_attribute) + "=" + escapeDN(credentials.getUsername()) |  | ||||||
|             + "," + user_base_dn; |  | ||||||
|  |  | ||||||
|         try { |  | ||||||
|  |  | ||||||
|             // Bind as user |  | ||||||
|             try { |  | ||||||
|                 ldapConnection.bind( |  | ||||||
|                         LDAPConnection.LDAP_V3, |  | ||||||
|                         user_dn, |  | ||||||
|                         credentials.getPassword().getBytes("UTF-8") |  | ||||||
|                 ); |  | ||||||
|             } |  | ||||||
|             catch (UnsupportedEncodingException e) { |  | ||||||
|                 throw new GuacamoleException(e); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|         } |  | ||||||
|         catch (LDAPException e) { |  | ||||||
|             logger.debug("LDAP bind failed.", e); |  | ||||||
|             return null; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Get config base DN |  | ||||||
|         String config_base_dn = environment.getRequiredProperty( |  | ||||||
|                 LDAPGuacamoleProperties.LDAP_CONFIG_BASE_DN |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         // Pull all connections |  | ||||||
|         try { |  | ||||||
|  |  | ||||||
|             // Find all guac configs for this user |  | ||||||
|             LDAPSearchResults results = ldapConnection.search( |  | ||||||
|                     config_base_dn, |  | ||||||
|                     LDAPConnection.SCOPE_SUB, |  | ||||||
|                     "(&(objectClass=guacConfigGroup)(member=" + escapeLDAPSearchFilter(user_dn) + "))", |  | ||||||
|                     null, |  | ||||||
|                     false |  | ||||||
|             ); |  | ||||||
|  |  | ||||||
|             // Add all configs |  | ||||||
|             Map<String, GuacamoleConfiguration> configs = new TreeMap<String, GuacamoleConfiguration>(); |  | ||||||
|             while (results.hasMore()) { |  | ||||||
|  |  | ||||||
|                 LDAPEntry entry = results.next(); |  | ||||||
|  |  | ||||||
|                 // New empty configuration |  | ||||||
|                 GuacamoleConfiguration config = new GuacamoleConfiguration(); |  | ||||||
|  |  | ||||||
|                 // Get CN |  | ||||||
|                 LDAPAttribute cn = entry.getAttribute("cn"); |  | ||||||
|                 if (cn == null) |  | ||||||
|                     throw new GuacamoleException("guacConfigGroup without cn"); |  | ||||||
|  |  | ||||||
|                 // Get protocol |  | ||||||
|                 LDAPAttribute protocol = entry.getAttribute("guacConfigProtocol"); |  | ||||||
|                 if (protocol == null) |  | ||||||
|                     throw new GuacamoleException("guacConfigGroup without guacConfigProtocol"); |  | ||||||
|  |  | ||||||
|                 // Set protocol |  | ||||||
|                 config.setProtocol(protocol.getStringValue()); |  | ||||||
|  |  | ||||||
|                 // Get parameters, if any |  | ||||||
|                 LDAPAttribute parameterAttribute = entry.getAttribute("guacConfigParameter"); |  | ||||||
|                 if (parameterAttribute != null) { |  | ||||||
|  |  | ||||||
|                     // For each parameter |  | ||||||
|                     Enumeration<?> parameters = parameterAttribute.getStringValues(); |  | ||||||
|                     while (parameters.hasMoreElements()) { |  | ||||||
|  |  | ||||||
|                         String parameter = (String) parameters.nextElement(); |  | ||||||
|  |  | ||||||
|                         // Parse parameter |  | ||||||
|                         int equals = parameter.indexOf('='); |  | ||||||
|                         if (equals != -1) { |  | ||||||
|  |  | ||||||
|                             // Parse name |  | ||||||
|                             String name = parameter.substring(0, equals); |  | ||||||
|                             String value = parameter.substring(equals+1); |  | ||||||
|  |  | ||||||
|                             config.setParameter(name, value); |  | ||||||
|  |  | ||||||
|                         } |  | ||||||
|  |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 // Store config by CN |  | ||||||
|                 configs.put(cn.getStringValue(), config); |  | ||||||
|  |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // Disconnect |  | ||||||
|             ldapConnection.disconnect(); |  | ||||||
|             return configs; |  | ||||||
|  |  | ||||||
|         } |  | ||||||
|         catch (LDAPException e) { |  | ||||||
|             throw new GuacamoleServerException("Error while querying for connections.", e); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public UserContext updateUserContext(UserContext context, | ||||||
|  |             AuthenticatedUser authenticatedUser) throws GuacamoleException { | ||||||
|  |         return context; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,245 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (C) 2015 Glyptodon LLC | ||||||
|  |  * | ||||||
|  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  |  * of this software and associated documentation files (the "Software"), to deal | ||||||
|  |  * in the Software without restriction, including without limitation the rights | ||||||
|  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  |  * copies of the Software, and to permit persons to whom the Software is | ||||||
|  |  * furnished to do so, subject to the following conditions: | ||||||
|  |  * | ||||||
|  |  * The above copyright notice and this permission notice shall be included in | ||||||
|  |  * all copies or substantial portions of the Software. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  |  * THE SOFTWARE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package org.glyptodon.guacamole.auth.ldap; | ||||||
|  |  | ||||||
|  | import com.google.inject.Inject; | ||||||
|  | import com.google.inject.Provider; | ||||||
|  | import com.novell.ldap.LDAPConnection; | ||||||
|  | import com.novell.ldap.LDAPException; | ||||||
|  | import java.io.UnsupportedEncodingException; | ||||||
|  | import org.glyptodon.guacamole.auth.ldap.user.AuthenticatedUser; | ||||||
|  | import org.glyptodon.guacamole.auth.ldap.user.UserContext; | ||||||
|  | import org.glyptodon.guacamole.GuacamoleException; | ||||||
|  | import org.glyptodon.guacamole.net.auth.Credentials; | ||||||
|  | import org.glyptodon.guacamole.net.auth.credentials.CredentialsInfo; | ||||||
|  | import org.glyptodon.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException; | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Service providing convenience functions for the LDAP AuthenticationProvider | ||||||
|  |  * implementation. | ||||||
|  |  * | ||||||
|  |  * @author Michael Jumper | ||||||
|  |  */ | ||||||
|  | public class AuthenticationProviderService { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Logger for this class. | ||||||
|  |      */ | ||||||
|  |     private final Logger logger = LoggerFactory.getLogger(AuthenticationProviderService.class); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Service for escaping parts of LDAP queries. | ||||||
|  |      */ | ||||||
|  |     @Inject | ||||||
|  |     private EscapingService escapingService; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Service for retrieving LDAP server configuration information. | ||||||
|  |      */ | ||||||
|  |     @Inject | ||||||
|  |     private ConfigurationService confService; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Provider for AuthenticatedUser objects. | ||||||
|  |      */ | ||||||
|  |     @Inject | ||||||
|  |     private Provider<AuthenticatedUser> authenticatedUserProvider; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Provider for UserContext objects. | ||||||
|  |      */ | ||||||
|  |     @Inject | ||||||
|  |     private Provider<UserContext> userContextProvider; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Binds to the LDAP server using the provided Guacamole credentials. The | ||||||
|  |      * DN of the user is derived using the LDAP configuration properties | ||||||
|  |      * provided in guacamole.properties, as is the server hostname and port | ||||||
|  |      * information. | ||||||
|  |      * | ||||||
|  |      * @param credentials | ||||||
|  |      *     The credentials to use to bind to the LDAP server. | ||||||
|  |      * | ||||||
|  |      * @return | ||||||
|  |      *     A bound LDAP connection, or null if the connection could not be | ||||||
|  |      *     bound. | ||||||
|  |      * | ||||||
|  |      * @throws GuacamoleException | ||||||
|  |      *     If an error occurs while binding to the LDAP server. | ||||||
|  |      */ | ||||||
|  |     private LDAPConnection bindAs(Credentials credentials) | ||||||
|  |         throws GuacamoleException { | ||||||
|  |  | ||||||
|  |         LDAPConnection ldapConnection; | ||||||
|  |  | ||||||
|  |         // Require username | ||||||
|  |         if (credentials.getUsername() == null) { | ||||||
|  |             logger.debug("Anonymous bind is not currently allowed by the LDAP authentication provider."); | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Require password, and do not allow anonymous binding | ||||||
|  |         if (credentials.getPassword() == null | ||||||
|  |                 || credentials.getPassword().length() == 0) { | ||||||
|  |             logger.debug("Anonymous bind is not currently allowed by the LDAP authentication provider."); | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Connect to LDAP server | ||||||
|  |         try { | ||||||
|  |             ldapConnection = new LDAPConnection(); | ||||||
|  |             ldapConnection.connect( | ||||||
|  |                 confService.getServerHostname(), | ||||||
|  |                 confService.getServerPort() | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |         catch (LDAPException e) { | ||||||
|  |             logger.error("Unable to connect to LDAP server: {}", e.getMessage()); | ||||||
|  |             logger.debug("Failed to connect to LDAP server.", e); | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Bind using provided credentials | ||||||
|  |         try { | ||||||
|  |  | ||||||
|  |             // Construct user DN | ||||||
|  |             String userDN = | ||||||
|  |                         escapingService.escapeDN(confService.getUsernameAttribute()) | ||||||
|  |                 + "=" + escapingService.escapeDN(credentials.getUsername()) | ||||||
|  |                 + "," + confService.getUserBaseDN(); | ||||||
|  |  | ||||||
|  |             // Bind as user | ||||||
|  |             try { | ||||||
|  |                 ldapConnection.bind(LDAPConnection.LDAP_V3, userDN, | ||||||
|  |                         credentials.getPassword().getBytes("UTF-8")); | ||||||
|  |             } | ||||||
|  |             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); | ||||||
|  |                 return null; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // Disconnect if an error occurs during bind | ||||||
|  |             catch (LDAPException e) { | ||||||
|  |                 ldapConnection.disconnect(); | ||||||
|  |                 throw e; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         catch (LDAPException e) { | ||||||
|  |             logger.debug("LDAP bind failed.", e); | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return ldapConnection; | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public AuthenticatedUser authenticateUser(Credentials credentials) | ||||||
|  |             throws GuacamoleException { | ||||||
|  |  | ||||||
|  |         // Attempt bind | ||||||
|  |         LDAPConnection ldapConnection = bindAs(credentials); | ||||||
|  |         if (ldapConnection == null) | ||||||
|  |             throw new GuacamoleInsufficientCredentialsException("Permission denied.", CredentialsInfo.USERNAME_PASSWORD); | ||||||
|  |  | ||||||
|  |         try { | ||||||
|  |  | ||||||
|  |             // Return AuthenticatedUser if bind succeeds | ||||||
|  |             AuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); | ||||||
|  |             authenticatedUser.init(credentials); | ||||||
|  |             return authenticatedUser; | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Always disconnect | ||||||
|  |         finally { | ||||||
|  |  | ||||||
|  |             // Attempt disconnect | ||||||
|  |             try { | ||||||
|  |                 ldapConnection.disconnect(); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // Warn if disconnect unexpectedly fails | ||||||
|  |             catch (LDAPException e) { | ||||||
|  |                 logger.warn("Unable to disconnect from LDAP server: {}", e.getMessage()); | ||||||
|  |                 logger.debug("LDAP disconnect failed.", e); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns a UserContext object initialized with data accessible to the | ||||||
|  |      * given AuthenticatedUser. | ||||||
|  |      * | ||||||
|  |      * @param authenticatedUser | ||||||
|  |      *     The AuthenticatedUser to retrieve data for. | ||||||
|  |      * | ||||||
|  |      * @return | ||||||
|  |      *     A UserContext object initialized with data accessible to the given | ||||||
|  |      *     AuthenticatedUser. | ||||||
|  |      * | ||||||
|  |      * @throws GuacamoleException | ||||||
|  |      *     If the UserContext cannot be created due to an error. | ||||||
|  |      */ | ||||||
|  |     public UserContext getUserContext(org.glyptodon.guacamole.net.auth.AuthenticatedUser authenticatedUser) | ||||||
|  |             throws GuacamoleException { | ||||||
|  |  | ||||||
|  |         // Bind using credentials associated with AuthenticatedUser | ||||||
|  |         Credentials credentials = authenticatedUser.getCredentials(); | ||||||
|  |         LDAPConnection ldapConnection = bindAs(credentials); | ||||||
|  |         if (ldapConnection == null) | ||||||
|  |             return null; | ||||||
|  |  | ||||||
|  |         try { | ||||||
|  |  | ||||||
|  |             // Build user context by querying LDAP | ||||||
|  |             UserContext userContext = userContextProvider.get(); | ||||||
|  |             userContext.init(authenticatedUser, ldapConnection); | ||||||
|  |             return userContext; | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Always disconnect | ||||||
|  |         finally { | ||||||
|  |  | ||||||
|  |             // Attempt disconnect | ||||||
|  |             try { | ||||||
|  |                 ldapConnection.disconnect(); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // Warn if disconnect unexpectedly fails | ||||||
|  |             catch (LDAPException e) { | ||||||
|  |                 logger.warn("Unable to disconnect from LDAP server: {}", e.getMessage()); | ||||||
|  |                 logger.debug("LDAP disconnect failed.", e); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,134 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (C) 2015 Glyptodon LLC | ||||||
|  |  * | ||||||
|  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  |  * of this software and associated documentation files (the "Software"), to deal | ||||||
|  |  * in the Software without restriction, including without limitation the rights | ||||||
|  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  |  * copies of the Software, and to permit persons to whom the Software is | ||||||
|  |  * furnished to do so, subject to the following conditions: | ||||||
|  |  * | ||||||
|  |  * The above copyright notice and this permission notice shall be included in | ||||||
|  |  * all copies or substantial portions of the Software. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  |  * THE SOFTWARE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package org.glyptodon.guacamole.auth.ldap; | ||||||
|  |  | ||||||
|  | import com.google.inject.Inject; | ||||||
|  | import org.glyptodon.guacamole.GuacamoleException; | ||||||
|  | import org.glyptodon.guacamole.environment.Environment; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Service for retrieving configuration information regarding the LDAP server. | ||||||
|  |  * | ||||||
|  |  * @author Michael Jumper | ||||||
|  |  */ | ||||||
|  | public class ConfigurationService { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * The Guacamole server environment. | ||||||
|  |      */ | ||||||
|  |     @Inject | ||||||
|  |     private Environment environment; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns the hostname of the LDAP server as configured with | ||||||
|  |      * guacamole.properties. By default, this will be "localhost". | ||||||
|  |      * | ||||||
|  |      * @return | ||||||
|  |      *     The hostname of the LDAP server, as configured with | ||||||
|  |      *     guacamole.properties. | ||||||
|  |      * | ||||||
|  |      * @throws GuacamoleException | ||||||
|  |      *     If guacamole.properties cannot be parsed. | ||||||
|  |      */ | ||||||
|  |     public String getServerHostname() throws GuacamoleException { | ||||||
|  |         return environment.getProperty( | ||||||
|  |             LDAPGuacamoleProperties.LDAP_HOSTNAME, | ||||||
|  |             "localhost" | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns the port of the LDAP server configured with | ||||||
|  |      * guacamole.properties. By default, this will be 389 - the standard LDAP | ||||||
|  |      * port. | ||||||
|  |      * | ||||||
|  |      * @return | ||||||
|  |      *     The port of the LDAP server, as configured with | ||||||
|  |      *     guacamole.properties. | ||||||
|  |      * | ||||||
|  |      * @throws GuacamoleException | ||||||
|  |      *     If guacamole.properties cannot be parsed. | ||||||
|  |      */ | ||||||
|  |     public int getServerPort() throws GuacamoleException { | ||||||
|  |         return environment.getProperty( | ||||||
|  |             LDAPGuacamoleProperties.LDAP_PORT, | ||||||
|  |             389 | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns the username attribute which should be used to query and bind | ||||||
|  |      * users using the LDAP directory. By default, this will be "uid" - a | ||||||
|  |      * common attribute used for this purpose. | ||||||
|  |      * | ||||||
|  |      * @return | ||||||
|  |      *     The username attribute which should be used to query and bind users | ||||||
|  |      *     using the LDAP directory. | ||||||
|  |      * | ||||||
|  |      * @throws GuacamoleException | ||||||
|  |      *     If guacamole.properties cannot be parsed. | ||||||
|  |      */ | ||||||
|  |     public String getUsernameAttribute() throws GuacamoleException { | ||||||
|  |         return environment.getProperty( | ||||||
|  |             LDAPGuacamoleProperties.LDAP_USERNAME_ATTRIBUTE, | ||||||
|  |             "uid" | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns the base DN under which all Guacamole users will be stored | ||||||
|  |      * within the LDAP directory. | ||||||
|  |      * | ||||||
|  |      * @return | ||||||
|  |      *     The base DN under which all Guacamole users will be stored within | ||||||
|  |      *     the LDAP directory. | ||||||
|  |      * | ||||||
|  |      * @throws GuacamoleException | ||||||
|  |      *     If guacamole.properties cannot be parsed, or if the user base DN | ||||||
|  |      *     property is not specified. | ||||||
|  |      */ | ||||||
|  |     public String getUserBaseDN() throws GuacamoleException { | ||||||
|  |         return environment.getRequiredProperty( | ||||||
|  |             LDAPGuacamoleProperties.LDAP_USER_BASE_DN | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns the base DN under which all Guacamole configurations | ||||||
|  |      * (connections) will be stored within the LDAP directory. | ||||||
|  |      * | ||||||
|  |      * @return | ||||||
|  |      *     The base DN under which all Guacamole configurations will be stored | ||||||
|  |      *     within the LDAP directory. | ||||||
|  |      * | ||||||
|  |      * @throws GuacamoleException | ||||||
|  |      *     If guacamole.properties cannot be parsed, or if the configuration | ||||||
|  |      *     base DN property is not specified. | ||||||
|  |      */ | ||||||
|  |     public String getConfigurationBaseDN() throws GuacamoleException { | ||||||
|  |         return environment.getRequiredProperty( | ||||||
|  |             LDAPGuacamoleProperties.LDAP_CONFIG_BASE_DN | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,125 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (C) 2015 Glyptodon LLC | ||||||
|  |  * | ||||||
|  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  |  * of this software and associated documentation files (the "Software"), to deal | ||||||
|  |  * in the Software without restriction, including without limitation the rights | ||||||
|  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  |  * copies of the Software, and to permit persons to whom the Software is | ||||||
|  |  * furnished to do so, subject to the following conditions: | ||||||
|  |  * | ||||||
|  |  * The above copyright notice and this permission notice shall be included in | ||||||
|  |  * all copies or substantial portions of the Software. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  |  * THE SOFTWARE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package org.glyptodon.guacamole.auth.ldap; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Service for escaping LDAP filters, distinguished names (DN's), etc. | ||||||
|  |  * | ||||||
|  |  * @author Michael Jumper | ||||||
|  |  */ | ||||||
|  | public class EscapingService { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Escapes the given string for use within an LDAP search filter. This | ||||||
|  |      * implementation is provided courtesy of OWASP: | ||||||
|  |      *  | ||||||
|  |      * https://www.owasp.org/index.php/Preventing_LDAP_Injection_in_Java | ||||||
|  |      * | ||||||
|  |      * @param filter | ||||||
|  |      *     The string to escape such that it has no special meaning within an | ||||||
|  |      *     LDAP search filter. | ||||||
|  |      * | ||||||
|  |      * @return | ||||||
|  |      *     The escaped string, safe for use within an LDAP search filter. | ||||||
|  |      */ | ||||||
|  |     public String escapeLDAPSearchFilter(String filter) { | ||||||
|  |         StringBuilder sb = new StringBuilder(); | ||||||
|  |         for (int i = 0; i < filter.length(); i++) { | ||||||
|  |             char curChar = filter.charAt(i); | ||||||
|  |             switch (curChar) { | ||||||
|  |                 case '\\': | ||||||
|  |                     sb.append("\\5c"); | ||||||
|  |                     break; | ||||||
|  |                 case '*': | ||||||
|  |                     sb.append("\\2a"); | ||||||
|  |                     break; | ||||||
|  |                 case '(': | ||||||
|  |                     sb.append("\\28"); | ||||||
|  |                     break; | ||||||
|  |                 case ')': | ||||||
|  |                     sb.append("\\29"); | ||||||
|  |                     break; | ||||||
|  |                 case '\u0000': | ||||||
|  |                     sb.append("\\00"); | ||||||
|  |                     break; | ||||||
|  |                 default: | ||||||
|  |                     sb.append(curChar); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return sb.toString(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Escapes the given string such that it is safe for use within an LDAP | ||||||
|  |      * distinguished name (DN). This implementation is provided courtesy of | ||||||
|  |      * OWASP: | ||||||
|  |      *  | ||||||
|  |      * https://www.owasp.org/index.php/Preventing_LDAP_Injection_in_Java | ||||||
|  |      * | ||||||
|  |      * @param name | ||||||
|  |      *     The string to escape such that it has no special meaning within an | ||||||
|  |      *     LDAP DN. | ||||||
|  |      * | ||||||
|  |      * @return | ||||||
|  |      *     The escaped string, safe for use within an LDAP DN. | ||||||
|  |      */ | ||||||
|  |     public String escapeDN(String name) { | ||||||
|  |         StringBuilder sb = new StringBuilder(); | ||||||
|  |         if ((name.length() > 0) && ((name.charAt(0) == ' ') || (name.charAt(0) == '#'))) { | ||||||
|  |             sb.append('\\'); // add the leading backslash if needed | ||||||
|  |         } | ||||||
|  |         for (int i = 0; i < name.length(); i++) { | ||||||
|  |             char curChar = name.charAt(i); | ||||||
|  |             switch (curChar) { | ||||||
|  |                 case '\\': | ||||||
|  |                     sb.append("\\\\"); | ||||||
|  |                     break; | ||||||
|  |                 case ',': | ||||||
|  |                     sb.append("\\,"); | ||||||
|  |                     break; | ||||||
|  |                 case '+': | ||||||
|  |                     sb.append("\\+"); | ||||||
|  |                     break; | ||||||
|  |                 case '"': | ||||||
|  |                     sb.append("\\\""); | ||||||
|  |                     break; | ||||||
|  |                 case '<': | ||||||
|  |                     sb.append("\\<"); | ||||||
|  |                     break; | ||||||
|  |                 case '>': | ||||||
|  |                     sb.append("\\>"); | ||||||
|  |                     break; | ||||||
|  |                 case ';': | ||||||
|  |                     sb.append("\\;"); | ||||||
|  |                     break; | ||||||
|  |                 default: | ||||||
|  |                     sb.append(curChar); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if ((name.length() > 1) && (name.charAt(name.length() - 1) == ' ')) { | ||||||
|  |             sb.insert(sb.length() - 1, '\\'); // add the trailing backslash if needed | ||||||
|  |         } | ||||||
|  |         return sb.toString(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,88 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (C) 2015 Glyptodon LLC | ||||||
|  |  * | ||||||
|  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  |  * of this software and associated documentation files (the "Software"), to deal | ||||||
|  |  * in the Software without restriction, including without limitation the rights | ||||||
|  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  |  * copies of the Software, and to permit persons to whom the Software is | ||||||
|  |  * furnished to do so, subject to the following conditions: | ||||||
|  |  * | ||||||
|  |  * The above copyright notice and this permission notice shall be included in | ||||||
|  |  * all copies or substantial portions of the Software. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  |  * THE SOFTWARE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package org.glyptodon.guacamole.auth.ldap; | ||||||
|  |  | ||||||
|  | import com.google.inject.AbstractModule; | ||||||
|  | import org.glyptodon.guacamole.auth.ldap.connection.ConnectionService; | ||||||
|  | import org.glyptodon.guacamole.auth.ldap.user.UserService; | ||||||
|  | import org.glyptodon.guacamole.GuacamoleException; | ||||||
|  | import org.glyptodon.guacamole.environment.Environment; | ||||||
|  | import org.glyptodon.guacamole.environment.LocalEnvironment; | ||||||
|  | import org.glyptodon.guacamole.net.auth.AuthenticationProvider; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Guice module which configures LDAP-specific injections. | ||||||
|  |  * | ||||||
|  |  * @author Michael Jumper | ||||||
|  |  */ | ||||||
|  | public class LDAPAuthenticationProviderModule extends AbstractModule { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Guacamole server environment. | ||||||
|  |      */ | ||||||
|  |     private final Environment environment; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * A reference to the LDAPAuthenticationProvider on behalf of which this | ||||||
|  |      * module has configured injection. | ||||||
|  |      */ | ||||||
|  |     private final AuthenticationProvider authProvider; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Creates a new LDAP authentication provider module which configures | ||||||
|  |      * injection for the LDAPAuthenticationProvider. | ||||||
|  |      * | ||||||
|  |      * @param authProvider | ||||||
|  |      *     The AuthenticationProvider for which injection is being configured. | ||||||
|  |      * | ||||||
|  |      * @throws GuacamoleException | ||||||
|  |      *     If an error occurs while retrieving the Guacamole server | ||||||
|  |      *     environment. | ||||||
|  |      */ | ||||||
|  |     public LDAPAuthenticationProviderModule(AuthenticationProvider authProvider) | ||||||
|  |             throws GuacamoleException { | ||||||
|  |  | ||||||
|  |         // Get local environment | ||||||
|  |         this.environment = new LocalEnvironment(); | ||||||
|  |  | ||||||
|  |         // Store associated auth provider | ||||||
|  |         this.authProvider = authProvider; | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected void configure() { | ||||||
|  |  | ||||||
|  |         // Bind core implementations of guacamole-ext classes | ||||||
|  |         bind(AuthenticationProvider.class).toInstance(authProvider); | ||||||
|  |         bind(Environment.class).toInstance(environment); | ||||||
|  |  | ||||||
|  |         // Bind LDAP-specific services | ||||||
|  |         bind(ConfigurationService.class); | ||||||
|  |         bind(ConnectionService.class); | ||||||
|  |         bind(EscapingService.class); | ||||||
|  |         bind(UserService.class); | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -20,7 +20,7 @@ | |||||||
|  * THE SOFTWARE. |  * THE SOFTWARE. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package net.sourceforge.guacamole.net.auth.ldap.properties; | package org.glyptodon.guacamole.auth.ldap; | ||||||
| 
 | 
 | ||||||
| import org.glyptodon.guacamole.properties.IntegerGuacamoleProperty; | import org.glyptodon.guacamole.properties.IntegerGuacamoleProperty; | ||||||
| import org.glyptodon.guacamole.properties.StringGuacamoleProperty; | import org.glyptodon.guacamole.properties.StringGuacamoleProperty; | ||||||
| @@ -0,0 +1,173 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (C) 2015 Glyptodon LLC | ||||||
|  |  * | ||||||
|  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  |  * of this software and associated documentation files (the "Software"), to deal | ||||||
|  |  * in the Software without restriction, including without limitation the rights | ||||||
|  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  |  * copies of the Software, and to permit persons to whom the Software is | ||||||
|  |  * furnished to do so, subject to the following conditions: | ||||||
|  |  * | ||||||
|  |  * The above copyright notice and this permission notice shall be included in | ||||||
|  |  * all copies or substantial portions of the Software. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  |  * THE SOFTWARE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package org.glyptodon.guacamole.auth.ldap.connection; | ||||||
|  |  | ||||||
|  | import com.google.inject.Inject; | ||||||
|  | import com.novell.ldap.LDAPAttribute; | ||||||
|  | import com.novell.ldap.LDAPConnection; | ||||||
|  | import com.novell.ldap.LDAPEntry; | ||||||
|  | import com.novell.ldap.LDAPException; | ||||||
|  | import com.novell.ldap.LDAPSearchResults; | ||||||
|  | import java.util.Enumeration; | ||||||
|  | import java.util.HashMap; | ||||||
|  | import java.util.Map; | ||||||
|  | import net.sourceforge.guacamole.net.auth.ldap.LDAPAuthenticationProvider; | ||||||
|  | import org.glyptodon.guacamole.auth.ldap.ConfigurationService; | ||||||
|  | import org.glyptodon.guacamole.auth.ldap.EscapingService; | ||||||
|  | import org.glyptodon.guacamole.GuacamoleException; | ||||||
|  | import org.glyptodon.guacamole.GuacamoleServerException; | ||||||
|  | import org.glyptodon.guacamole.net.auth.Connection; | ||||||
|  | import org.glyptodon.guacamole.net.auth.simple.SimpleConnection; | ||||||
|  | import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Service for querying the connections available to a particular Guacamole | ||||||
|  |  * user according to an LDAP directory. | ||||||
|  |  * | ||||||
|  |  * @author Michael Jumper | ||||||
|  |  */ | ||||||
|  | public class ConnectionService { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Logger for this class. | ||||||
|  |      */ | ||||||
|  |     private final Logger logger = LoggerFactory.getLogger(ConnectionService.class); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Service for escaping parts of LDAP queries. | ||||||
|  |      */ | ||||||
|  |     @Inject | ||||||
|  |     private EscapingService escapingService; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Service for retrieving LDAP server configuration information. | ||||||
|  |      */ | ||||||
|  |     @Inject | ||||||
|  |     private ConfigurationService confService; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns all Guacamole connections 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 connections accessible to the user currently bound under the | ||||||
|  |      *     given LDAP connection, as a map of connection identifier to | ||||||
|  |      *     corresponding connection object. | ||||||
|  |      * | ||||||
|  |      * @throws GuacamoleException | ||||||
|  |      *     If an error occurs preventing retrieval of connections. | ||||||
|  |      */ | ||||||
|  |     public Map<String, Connection> getConnections(LDAPConnection ldapConnection) | ||||||
|  |             throws GuacamoleException { | ||||||
|  |  | ||||||
|  |         try { | ||||||
|  |  | ||||||
|  |             // Pull the current user DN from the LDAP connection | ||||||
|  |             String userDN = ldapConnection.getAuthenticationDN(); | ||||||
|  |  | ||||||
|  |             // Find all Guacamole connections for the given user | ||||||
|  |             LDAPSearchResults results = ldapConnection.search( | ||||||
|  |                 confService.getConfigurationBaseDN(), | ||||||
|  |                 LDAPConnection.SCOPE_SUB, | ||||||
|  |                 "(&(objectClass=guacConfigGroup)(member=" + escapingService.escapeLDAPSearchFilter(userDN) + "))", | ||||||
|  |                 null, | ||||||
|  |                 false | ||||||
|  |             ); | ||||||
|  |  | ||||||
|  |             // Produce connections for each readable configuration | ||||||
|  |             Map<String, Connection> connections = new HashMap<String, Connection>(); | ||||||
|  |             while (results.hasMore()) { | ||||||
|  |  | ||||||
|  |                 LDAPEntry entry = results.next(); | ||||||
|  |  | ||||||
|  |                 // Get common name (CN) | ||||||
|  |                 LDAPAttribute cn = entry.getAttribute("cn"); | ||||||
|  |                 if (cn == null) { | ||||||
|  |                     logger.warn("guacConfigGroup is missing a cn."); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 // Get associated protocol | ||||||
|  |                 LDAPAttribute protocol = entry.getAttribute("guacConfigProtocol"); | ||||||
|  |                 if (protocol == null) { | ||||||
|  |                     logger.warn("guacConfigGroup \"{}\" is missing the " | ||||||
|  |                               + "required \"guacConfigProtocol\" attribute.", | ||||||
|  |                             cn.getStringValue()); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 // Set protocol | ||||||
|  |                 GuacamoleConfiguration config = new GuacamoleConfiguration(); | ||||||
|  |                 config.setProtocol(protocol.getStringValue()); | ||||||
|  |  | ||||||
|  |                 // Get parameters, if any | ||||||
|  |                 LDAPAttribute parameterAttribute = entry.getAttribute("guacConfigParameter"); | ||||||
|  |                 if (parameterAttribute != null) { | ||||||
|  |  | ||||||
|  |                     // For each parameter | ||||||
|  |                     Enumeration<?> parameters = parameterAttribute.getStringValues(); | ||||||
|  |                     while (parameters.hasMoreElements()) { | ||||||
|  |  | ||||||
|  |                         String parameter = (String) parameters.nextElement(); | ||||||
|  |  | ||||||
|  |                         // Parse parameter | ||||||
|  |                         int equals = parameter.indexOf('='); | ||||||
|  |                         if (equals != -1) { | ||||||
|  |  | ||||||
|  |                             // Parse name | ||||||
|  |                             String name = parameter.substring(0, equals); | ||||||
|  |                             String value = parameter.substring(equals+1); | ||||||
|  |  | ||||||
|  |                             config.setParameter(name, value); | ||||||
|  |  | ||||||
|  |                         } | ||||||
|  |  | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 // Store connection using cn for both identifier and name | ||||||
|  |                 String name = cn.getStringValue(); | ||||||
|  |                 Connection connection = new SimpleConnection(name, name, config); | ||||||
|  |                 connection.setParentIdentifier(LDAPAuthenticationProvider.ROOT_CONNECTION_GROUP); | ||||||
|  |                 connections.put(name, connection); | ||||||
|  |  | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // Return map of all connections | ||||||
|  |             return connections; | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         catch (LDAPException e) { | ||||||
|  |             throw new GuacamoleServerException("Error while querying for connections.", e); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,71 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (C) 2015 Glyptodon LLC | ||||||
|  |  * | ||||||
|  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  |  * of this software and associated documentation files (the "Software"), to deal | ||||||
|  |  * in the Software without restriction, including without limitation the rights | ||||||
|  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  |  * copies of the Software, and to permit persons to whom the Software is | ||||||
|  |  * furnished to do so, subject to the following conditions: | ||||||
|  |  * | ||||||
|  |  * The above copyright notice and this permission notice shall be included in | ||||||
|  |  * all copies or substantial portions of the Software. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  |  * THE SOFTWARE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package org.glyptodon.guacamole.auth.ldap.user; | ||||||
|  |  | ||||||
|  | import com.google.inject.Inject; | ||||||
|  | import org.glyptodon.guacamole.net.auth.AbstractAuthenticatedUser; | ||||||
|  | import org.glyptodon.guacamole.net.auth.AuthenticationProvider; | ||||||
|  | import org.glyptodon.guacamole.net.auth.Credentials; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * An LDAP-specific implementation of AuthenticatedUser, associating a | ||||||
|  |  * particular set of credentials with the LDAP authentication provider. | ||||||
|  |  * | ||||||
|  |  * @author Michael Jumper | ||||||
|  |  */ | ||||||
|  | public class AuthenticatedUser extends AbstractAuthenticatedUser { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Reference to the authentication provider associated with this | ||||||
|  |      * authenticated user. | ||||||
|  |      */ | ||||||
|  |     @Inject | ||||||
|  |     private AuthenticationProvider authProvider; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * The credentials provided when this user was authenticated. | ||||||
|  |      */ | ||||||
|  |     private Credentials credentials; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Initializes this AuthenticatedUser using the given credentials. | ||||||
|  |      * | ||||||
|  |      * @param credentials | ||||||
|  |      *     The credentials provided when this user was authenticated. | ||||||
|  |      */ | ||||||
|  |     public void init(Credentials credentials) { | ||||||
|  |         this.credentials = credentials; | ||||||
|  |         setIdentifier(credentials.getUsername()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public AuthenticationProvider getAuthenticationProvider() { | ||||||
|  |         return authProvider; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Credentials getCredentials() { | ||||||
|  |         return credentials; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,212 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (C) 2015 Glyptodon LLC | ||||||
|  |  * | ||||||
|  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  |  * of this software and associated documentation files (the "Software"), to deal | ||||||
|  |  * in the Software without restriction, including without limitation the rights | ||||||
|  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  |  * copies of the Software, and to permit persons to whom the Software is | ||||||
|  |  * furnished to do so, subject to the following conditions: | ||||||
|  |  * | ||||||
|  |  * The above copyright notice and this permission notice shall be included in | ||||||
|  |  * all copies or substantial portions of the Software. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  |  * THE SOFTWARE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package org.glyptodon.guacamole.auth.ldap.user; | ||||||
|  |  | ||||||
|  | import com.google.inject.Inject; | ||||||
|  | import com.novell.ldap.LDAPConnection; | ||||||
|  | import java.util.Collection; | ||||||
|  | import java.util.Collections; | ||||||
|  | import net.sourceforge.guacamole.net.auth.ldap.LDAPAuthenticationProvider; | ||||||
|  | import org.glyptodon.guacamole.auth.ldap.connection.ConnectionService; | ||||||
|  | import org.glyptodon.guacamole.GuacamoleException; | ||||||
|  | import org.glyptodon.guacamole.form.Form; | ||||||
|  | import org.glyptodon.guacamole.net.auth.ActiveConnection; | ||||||
|  | import org.glyptodon.guacamole.net.auth.AuthenticatedUser; | ||||||
|  | import org.glyptodon.guacamole.net.auth.AuthenticationProvider; | ||||||
|  | import org.glyptodon.guacamole.net.auth.Connection; | ||||||
|  | import org.glyptodon.guacamole.net.auth.ConnectionGroup; | ||||||
|  | import org.glyptodon.guacamole.net.auth.Directory; | ||||||
|  | import org.glyptodon.guacamole.net.auth.User; | ||||||
|  | import org.glyptodon.guacamole.net.auth.simple.SimpleConnectionGroup; | ||||||
|  | import org.glyptodon.guacamole.net.auth.simple.SimpleConnectionGroupDirectory; | ||||||
|  | import org.glyptodon.guacamole.net.auth.simple.SimpleDirectory; | ||||||
|  | import org.glyptodon.guacamole.net.auth.simple.SimpleUser; | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * An LDAP-specific implementation of UserContext which queries all Guacamole | ||||||
|  |  * connections and users from the LDAP directory. | ||||||
|  |  * | ||||||
|  |  * @author Michael Jumper | ||||||
|  |  */ | ||||||
|  | public class UserContext implements org.glyptodon.guacamole.net.auth.UserContext { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Logger for this class. | ||||||
|  |      */ | ||||||
|  |     private final Logger logger = LoggerFactory.getLogger(UserContext.class); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Service for retrieving Guacamole connections from the LDAP server. | ||||||
|  |      */ | ||||||
|  |     @Inject | ||||||
|  |     private ConnectionService connectionService; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Service for retrieving Guacamole users from the LDAP server. | ||||||
|  |      */ | ||||||
|  |     @Inject | ||||||
|  |     private UserService userService; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Reference to the AuthenticationProvider associated with this | ||||||
|  |      * UserContext. | ||||||
|  |      */ | ||||||
|  |     @Inject | ||||||
|  |     private AuthenticationProvider authProvider; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Reference to a User object representing the user whose access level | ||||||
|  |      * dictates the users and connections visible through this UserContext. | ||||||
|  |      */ | ||||||
|  |     private User self; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Directory containing all User objects accessible to the user associated | ||||||
|  |      * with this UserContext. | ||||||
|  |      */ | ||||||
|  |     private Directory<User> userDirectory; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Directory containing all Connection objects accessible to the user | ||||||
|  |      * associated with this UserContext. | ||||||
|  |      */ | ||||||
|  |     private Directory<Connection> connectionDirectory; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Directory containing all ConnectionGroup objects accessible to the user | ||||||
|  |      * associated with this UserContext. | ||||||
|  |      */ | ||||||
|  |     private Directory<ConnectionGroup> connectionGroupDirectory; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Reference to the root connection group. | ||||||
|  |      */ | ||||||
|  |     private ConnectionGroup rootGroup; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Initializes this UserContext using the provided AuthenticatedUser and | ||||||
|  |      * LDAPConnection. | ||||||
|  |      * | ||||||
|  |      * @param user | ||||||
|  |      *     The AuthenticatedUser representing the user that authenticated. This | ||||||
|  |      *     user may have been authenticated by a different authentication | ||||||
|  |      *     provider (not LDAP). | ||||||
|  |      * | ||||||
|  |      * @param ldapConnection | ||||||
|  |      *     The connection to the LDAP server to use when querying accessible | ||||||
|  |      *     Guacamole users and connections. | ||||||
|  |      * | ||||||
|  |      * @throws GuacamoleException | ||||||
|  |      *     If associated data stored within the LDAP directory cannot be | ||||||
|  |      *     queried due to an error. | ||||||
|  |      */ | ||||||
|  |     public void init(AuthenticatedUser user, LDAPConnection ldapConnection) | ||||||
|  |             throws GuacamoleException { | ||||||
|  |  | ||||||
|  |         // Query all accessible users | ||||||
|  |         userDirectory = new SimpleDirectory<User>( | ||||||
|  |             userService.getUsers(ldapConnection) | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         // Query all accessible connections | ||||||
|  |         connectionDirectory = new SimpleDirectory<Connection>( | ||||||
|  |             connectionService.getConnections(ldapConnection) | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         // Root group contains only connections | ||||||
|  |         rootGroup = new SimpleConnectionGroup( | ||||||
|  |             LDAPAuthenticationProvider.ROOT_CONNECTION_GROUP, | ||||||
|  |             LDAPAuthenticationProvider.ROOT_CONNECTION_GROUP, | ||||||
|  |             connectionDirectory.getIdentifiers(), | ||||||
|  |             Collections.<String>emptyList() | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         // Expose only the root group in the connection group directory | ||||||
|  |         connectionGroupDirectory = new SimpleConnectionGroupDirectory(Collections.singleton(rootGroup)); | ||||||
|  |  | ||||||
|  |         // Init self with basic permissions | ||||||
|  |         self = new SimpleUser( | ||||||
|  |             user.getIdentifier(), | ||||||
|  |             userDirectory.getIdentifiers(), | ||||||
|  |             connectionDirectory.getIdentifiers(), | ||||||
|  |             connectionGroupDirectory.getIdentifiers() | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public User self() { | ||||||
|  |         return self; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public AuthenticationProvider getAuthenticationProvider() { | ||||||
|  |         return authProvider; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Directory<User> getUserDirectory() throws GuacamoleException { | ||||||
|  |         return userDirectory; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Directory<Connection> getConnectionDirectory() | ||||||
|  |             throws GuacamoleException { | ||||||
|  |         return connectionDirectory; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Directory<ConnectionGroup> getConnectionGroupDirectory() | ||||||
|  |             throws GuacamoleException { | ||||||
|  |         return connectionGroupDirectory; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public ConnectionGroup getRootConnectionGroup() throws GuacamoleException { | ||||||
|  |         return rootGroup; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Directory<ActiveConnection> getActiveConnectionDirectory() | ||||||
|  |             throws GuacamoleException { | ||||||
|  |         return new SimpleDirectory<ActiveConnection>(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Collection<Form> getUserAttributes() { | ||||||
|  |         return Collections.<Form>emptyList(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Collection<Form> getConnectionAttributes() { | ||||||
|  |         return Collections.<Form>emptyList(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Collection<Form> getConnectionGroupAttributes() { | ||||||
|  |         return Collections.<Form>emptyList(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,129 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (C) 2015 Glyptodon LLC | ||||||
|  |  * | ||||||
|  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  |  * of this software and associated documentation files (the "Software"), to deal | ||||||
|  |  * in the Software without restriction, including without limitation the rights | ||||||
|  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  |  * copies of the Software, and to permit persons to whom the Software is | ||||||
|  |  * furnished to do so, subject to the following conditions: | ||||||
|  |  * | ||||||
|  |  * The above copyright notice and this permission notice shall be included in | ||||||
|  |  * all copies or substantial portions of the Software. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  |  * THE SOFTWARE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package org.glyptodon.guacamole.auth.ldap.user; | ||||||
|  |  | ||||||
|  | import com.google.inject.Inject; | ||||||
|  | import com.novell.ldap.LDAPAttribute; | ||||||
|  | import com.novell.ldap.LDAPConnection; | ||||||
|  | import com.novell.ldap.LDAPEntry; | ||||||
|  | import com.novell.ldap.LDAPException; | ||||||
|  | import com.novell.ldap.LDAPSearchResults; | ||||||
|  | import java.util.HashMap; | ||||||
|  | import java.util.Map; | ||||||
|  | import org.glyptodon.guacamole.auth.ldap.ConfigurationService; | ||||||
|  | import org.glyptodon.guacamole.auth.ldap.EscapingService; | ||||||
|  | import org.glyptodon.guacamole.GuacamoleException; | ||||||
|  | import org.glyptodon.guacamole.GuacamoleServerException; | ||||||
|  | import org.glyptodon.guacamole.net.auth.User; | ||||||
|  | import org.glyptodon.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. | ||||||
|  |  * | ||||||
|  |  * @author Michael Jumper | ||||||
|  |  */ | ||||||
|  | public class UserService { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Logger for this class. | ||||||
|  |      */ | ||||||
|  |     private final Logger logger = LoggerFactory.getLogger(UserService.class); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Service for escaping parts of LDAP queries. | ||||||
|  |      */ | ||||||
|  |     @Inject | ||||||
|  |     private EscapingService escapingService; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Service for retrieving LDAP server configuration information. | ||||||
|  |      */ | ||||||
|  |     @Inject | ||||||
|  |     private ConfigurationService confService; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 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 { | ||||||
|  |  | ||||||
|  |         try { | ||||||
|  |  | ||||||
|  |             // Get username attribute | ||||||
|  |             String usernameAttribute = confService.getUsernameAttribute(); | ||||||
|  |  | ||||||
|  |             // Find all Guacamole users underneath base DN | ||||||
|  |             LDAPSearchResults results = ldapConnection.search( | ||||||
|  |                 confService.getUserBaseDN(), | ||||||
|  |                 LDAPConnection.SCOPE_ONE, | ||||||
|  |                 "(&(objectClass=*)(" + escapingService.escapeLDAPSearchFilter(usernameAttribute) + "=*))", | ||||||
|  |                 null, | ||||||
|  |                 false | ||||||
|  |             ); | ||||||
|  |  | ||||||
|  |             // Read all visible users | ||||||
|  |             Map<String, User> users = new HashMap<String, User>(); | ||||||
|  |             while (results.hasMore()) { | ||||||
|  |  | ||||||
|  |                 LDAPEntry entry = results.next(); | ||||||
|  |  | ||||||
|  |                 // Get common name (CN) | ||||||
|  |                 LDAPAttribute username = entry.getAttribute(usernameAttribute); | ||||||
|  |                 if (username == null) { | ||||||
|  |                     logger.warn("Queried user is missing the username attribute \"{}\".", usernameAttribute); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 // Store connection using cn for both identifier and name | ||||||
|  |                 String identifier = username.getStringValue(); | ||||||
|  |                 users.put(identifier, new SimpleUser(identifier)); | ||||||
|  |  | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // Return map of all connections | ||||||
|  |             return users; | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         catch (LDAPException e) { | ||||||
|  |             throw new GuacamoleServerException("Error while querying users.", e); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -1,5 +1,5 @@ | |||||||
| /* | /* | ||||||
|  * Copyright (C) 2013 Glyptodon LLC |  * Copyright (C) 2015 Glyptodon LLC | ||||||
|  *  |  *  | ||||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy |  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  * of this software and associated documentation files (the "Software"), to deal |  * of this software and associated documentation files (the "Software"), to deal | ||||||
| @@ -28,7 +28,6 @@ import java.util.HashSet; | |||||||
| import java.util.Map; | import java.util.Map; | ||||||
| import java.util.Set; | import java.util.Set; | ||||||
| import org.glyptodon.guacamole.GuacamoleException; | import org.glyptodon.guacamole.GuacamoleException; | ||||||
| import org.glyptodon.guacamole.GuacamoleSecurityException; |  | ||||||
| import org.glyptodon.guacamole.net.auth.AbstractUser; | import org.glyptodon.guacamole.net.auth.AbstractUser; | ||||||
| import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; | import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; | ||||||
| import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; | import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; | ||||||
| @@ -41,6 +40,12 @@ import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet; | |||||||
|  */ |  */ | ||||||
| public class SimpleUser extends AbstractUser { | public class SimpleUser extends AbstractUser { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * All connection permissions granted to this user. | ||||||
|  |      */ | ||||||
|  |     private final Set<ObjectPermission> userPermissions = | ||||||
|  |             new HashSet<ObjectPermission>(); | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * All connection permissions granted to this user. |      * All connection permissions granted to this user. | ||||||
|      */ |      */ | ||||||
| @@ -59,6 +64,19 @@ public class SimpleUser extends AbstractUser { | |||||||
|     public SimpleUser() { |     public SimpleUser() { | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Creates a new SimpleUser having the given username and no permissions. | ||||||
|  |      * | ||||||
|  |      * @param username | ||||||
|  |      *     The username to assign to this SimpleUser. | ||||||
|  |      */ | ||||||
|  |     public SimpleUser(String username) { | ||||||
|  |  | ||||||
|  |         // Set username | ||||||
|  |         setIdentifier(username); | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Adds a new READ permission to the given set of permissions for each of |      * Adds a new READ permission to the given set of permissions for each of | ||||||
|      * the given identifiers. |      * the given identifiers. | ||||||
| @@ -89,6 +107,7 @@ public class SimpleUser extends AbstractUser { | |||||||
|      * |      * | ||||||
|      * @param username |      * @param username | ||||||
|      *     The username to assign to this SimpleUser. |      *     The username to assign to this SimpleUser. | ||||||
|  |      * | ||||||
|      * @param connectionIdentifiers |      * @param connectionIdentifiers | ||||||
|      *     The identifiers of all connections this user has READ access to. |      *     The identifiers of all connections this user has READ access to. | ||||||
|      * |      * | ||||||
| @@ -100,8 +119,7 @@ public class SimpleUser extends AbstractUser { | |||||||
|             Collection<String> connectionIdentifiers, |             Collection<String> connectionIdentifiers, | ||||||
|             Collection<String> connectionGroupIdentifiers) { |             Collection<String> connectionGroupIdentifiers) { | ||||||
|  |  | ||||||
|         // Set username |         this(username); | ||||||
|         setIdentifier(username); |  | ||||||
|  |  | ||||||
|         // Add permissions |         // Add permissions | ||||||
|         addReadPermissions(connectionPermissions,      connectionIdentifiers); |         addReadPermissions(connectionPermissions,      connectionIdentifiers); | ||||||
| @@ -109,6 +127,37 @@ public class SimpleUser extends AbstractUser { | |||||||
|  |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Creates a new SimpleUser having the given username and READ access to | ||||||
|  |      * the users, connections, and groups having the given identifiers. | ||||||
|  |      * | ||||||
|  |      * @param username | ||||||
|  |      *     The username to assign to this SimpleUser. | ||||||
|  |      * | ||||||
|  |      * @param userIdentifiers | ||||||
|  |      *     The identifiers of all users this user has READ access to. | ||||||
|  |      * | ||||||
|  |      * @param connectionIdentifiers | ||||||
|  |      *     The identifiers of all connections this user has READ access to. | ||||||
|  |      * | ||||||
|  |      * @param connectionGroupIdentifiers | ||||||
|  |      *     The identifiers of all connection groups this user has READ access | ||||||
|  |      *     to. | ||||||
|  |      */ | ||||||
|  |     public SimpleUser(String username, | ||||||
|  |             Collection<String> userIdentifiers, | ||||||
|  |             Collection<String> connectionIdentifiers, | ||||||
|  |             Collection<String> connectionGroupIdentifiers) { | ||||||
|  |  | ||||||
|  |         this(username); | ||||||
|  |  | ||||||
|  |         // Add permissions | ||||||
|  |         addReadPermissions(userPermissions,            userIdentifiers); | ||||||
|  |         addReadPermissions(connectionPermissions,      connectionIdentifiers); | ||||||
|  |         addReadPermissions(connectionGroupPermissions, connectionGroupIdentifiers); | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public Map<String, String> getAttributes() { |     public Map<String, String> getAttributes() { | ||||||
|         return Collections.<String, String>emptyMap(); |         return Collections.<String, String>emptyMap(); | ||||||
| @@ -140,7 +189,7 @@ public class SimpleUser extends AbstractUser { | |||||||
|     @Override |     @Override | ||||||
|     public ObjectPermissionSet getUserPermissions() |     public ObjectPermissionSet getUserPermissions() | ||||||
|             throws GuacamoleException { |             throws GuacamoleException { | ||||||
|         return new SimpleObjectPermissionSet(); |         return new SimpleObjectPermissionSet(userPermissions); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user