mirror of
				https://github.com/gyurix1968/guacamole-client.git
				synced 2025-10-30 16:43:22 +00:00 
			
		
		
		
	GUACAMOLE-839: Move authentication session management service to internal common SSO library.
This commit is contained in:
		| @@ -28,7 +28,7 @@ import javax.servlet.http.HttpServletRequest; | ||||
| import org.apache.guacamole.auth.saml.user.SAMLAuthenticatedUser; | ||||
| import org.apache.guacamole.GuacamoleException; | ||||
| import org.apache.guacamole.auth.saml.acs.AssertedIdentity; | ||||
| import org.apache.guacamole.auth.saml.acs.AuthenticationSessionManager; | ||||
| import org.apache.guacamole.auth.saml.acs.SAMLAuthenticationSessionManager; | ||||
| import org.apache.guacamole.auth.saml.acs.SAMLService; | ||||
| import org.apache.guacamole.auth.sso.SSOAuthenticationProviderService; | ||||
| import org.apache.guacamole.form.Field; | ||||
| @@ -61,7 +61,7 @@ public class AuthenticationProviderService implements SSOAuthenticationProviderS | ||||
|      * Manager of active SAML authentication attempts. | ||||
|      */ | ||||
|     @Inject | ||||
|     private AuthenticationSessionManager sessionManager; | ||||
|     private SAMLAuthenticationSessionManager sessionManager; | ||||
|  | ||||
|     /** | ||||
|      * Service for processing SAML requests/responses. | ||||
|   | ||||
| @@ -22,8 +22,7 @@ package org.apache.guacamole.auth.saml; | ||||
| import com.google.inject.AbstractModule; | ||||
| import org.apache.guacamole.auth.saml.conf.ConfigurationService; | ||||
| import org.apache.guacamole.auth.saml.acs.AssertionConsumerServiceResource; | ||||
| import org.apache.guacamole.auth.saml.acs.AuthenticationSessionManager; | ||||
| import org.apache.guacamole.auth.saml.acs.IdentifierGenerator; | ||||
| import org.apache.guacamole.auth.saml.acs.SAMLAuthenticationSessionManager; | ||||
| import org.apache.guacamole.auth.saml.acs.SAMLService; | ||||
|  | ||||
| /** | ||||
| @@ -34,9 +33,8 @@ public class SAMLAuthenticationProviderModule extends AbstractModule { | ||||
|     @Override | ||||
|     protected void configure() { | ||||
|         bind(AssertionConsumerServiceResource.class); | ||||
|         bind(AuthenticationSessionManager.class); | ||||
|         bind(ConfigurationService.class); | ||||
|         bind(IdentifierGenerator.class); | ||||
|         bind(SAMLAuthenticationSessionManager.class); | ||||
|         bind(SAMLService.class); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -56,7 +56,7 @@ public class AssertionConsumerServiceResource extends SSOResource { | ||||
|      * Manager of active SAML authentication attempts. | ||||
|      */ | ||||
|     @Inject | ||||
|     private AuthenticationSessionManager sessionManager; | ||||
|     private SAMLAuthenticationSessionManager sessionManager; | ||||
|  | ||||
|     /** | ||||
|      * Service for processing SAML requests/responses. | ||||
| @@ -107,7 +107,7 @@ public class AssertionConsumerServiceResource extends SSOResource { | ||||
|         try { | ||||
|  | ||||
|             // Validate and parse identity asserted by SAML IdP | ||||
|             AuthenticationSession session = saml.processResponse( | ||||
|             SAMLAuthenticationSession session = saml.processResponse( | ||||
|                     consumedRequest.getRequestURL().toString(), | ||||
|                     relayState, samlResponse); | ||||
|  | ||||
|   | ||||
| @@ -1,175 +0,0 @@ | ||||
| /* | ||||
|  * 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.saml.acs; | ||||
|  | ||||
| import com.google.common.base.Predicates; | ||||
| import com.google.inject.Inject; | ||||
| import com.google.inject.Singleton; | ||||
| import java.util.concurrent.ConcurrentHashMap; | ||||
| import java.util.concurrent.ConcurrentMap; | ||||
| import java.util.concurrent.Executors; | ||||
| import java.util.concurrent.ScheduledExecutorService; | ||||
| import java.util.concurrent.TimeUnit; | ||||
|  | ||||
| /** | ||||
|  * Manager service that temporarily stores SAML authentication attempts while | ||||
|  * the authentication flow is underway. Authentication attempts are represented | ||||
|  * as temporary authentication sessions, allowing authentication attempts to | ||||
|  * span multiple requests and redirects. Invalid or stale authentication | ||||
|  * sessions are automatically purged from storage. | ||||
|  */ | ||||
| @Singleton | ||||
| public class AuthenticationSessionManager { | ||||
|  | ||||
|     /** | ||||
|      * Generator of arbitrary, unique, unpredictable identifiers. | ||||
|      */ | ||||
|     @Inject | ||||
|     private IdentifierGenerator idGenerator; | ||||
|  | ||||
|     /** | ||||
|      * Map of authentication session identifiers to their associated | ||||
|      * {@link AuthenticationSession}. | ||||
|      */ | ||||
|     private final ConcurrentMap<String, AuthenticationSession> sessions = | ||||
|         new ConcurrentHashMap<>(); | ||||
|  | ||||
|     /** | ||||
|      * Executor service which runs the periodic cleanup task | ||||
|      */ | ||||
|     private final ScheduledExecutorService executor = | ||||
|             Executors.newScheduledThreadPool(1); | ||||
|  | ||||
|     /** | ||||
|      * Creates a new AuthenticationSessionManager that manages in-progress | ||||
|      * SAML authentication attempts. Invalid, stale sessions are automatically | ||||
|      * cleaned up. | ||||
|      */ | ||||
|     public AuthenticationSessionManager() { | ||||
|         executor.scheduleAtFixedRate(() -> { | ||||
|             sessions.values().removeIf(Predicates.not(AuthenticationSession::isValid)); | ||||
|         }, 1, 1, TimeUnit.MINUTES); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Resumes the Guacamole side of the authentication process that was | ||||
|      * previously deferred through a call to defer(). Once invoked, the | ||||
|      * provided value ceases to be valid for future calls to resume(). | ||||
|      * | ||||
|      * @param identifier | ||||
|      *     The unique string returned by the call to defer(). For convenience, | ||||
|      *     this value may safely be null. | ||||
|      * | ||||
|      * @return | ||||
|      *     The {@link AuthenticationSession} originally provided when defer() | ||||
|      *     was invoked, or null if the session is no longer valid or no such | ||||
|      *     value was returned by defer(). | ||||
|      */ | ||||
|     public AuthenticationSession resume(String identifier) { | ||||
|  | ||||
|         if (identifier != null) { | ||||
|             AuthenticationSession session = sessions.remove(identifier); | ||||
|             if (session != null && session.isValid()) | ||||
|                 return session; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the identity finally asserted by the SAML IdP at the end of the | ||||
|      * authentication process represented by the authentication session with | ||||
|      * the given identifier. If there is no such authentication session, or no | ||||
|      * valid identity has been asserted by the SAML IdP for that session, null | ||||
|      * is returned. | ||||
|      * | ||||
|      * @param identifier | ||||
|      *     The unique string returned by the call to defer(). For convenience, | ||||
|      *     this value may safely be null. | ||||
|      * | ||||
|      * @return | ||||
|      *     The identity finally asserted by the SAML IdP at the end of the | ||||
|      *     authentication process represented by the authentication session | ||||
|      *     with the given identifier, or null if there is no such identity. | ||||
|      */ | ||||
|     public AssertedIdentity getIdentity(String identifier) { | ||||
|  | ||||
|         AuthenticationSession session = resume(identifier); | ||||
|         if (session != null) | ||||
|             return session.getIdentity(); | ||||
|  | ||||
|         return null; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Defers the Guacamole side of authentication for the user having the | ||||
|      * given authentication session such that it may be later resumed through a | ||||
|      * call to resume(). If authentication is never resumed, the session will | ||||
|      * automatically be cleaned up after it ceases to be valid. | ||||
|      * | ||||
|      * This method will automatically generate a new identifier. | ||||
|      * | ||||
|      * @param session | ||||
|      *     The {@link AuthenticationSession} representing the in-progress SAML | ||||
|      *     authentication attempt. | ||||
|      * | ||||
|      * @return | ||||
|      *     A unique and unpredictable string that may be used to represent the | ||||
|      *     given session when calling resume(). | ||||
|      */ | ||||
|     public String defer(AuthenticationSession session) { | ||||
|         String identifier = idGenerator.generateIdentifier(); | ||||
|         sessions.put(identifier, session); | ||||
|         return identifier; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Defers the Guacamole side of authentication for the user having the | ||||
|      * given authentication session such that it may be later resumed through a | ||||
|      * call to resume(). If authentication is never resumed, the session will | ||||
|      * automatically be cleaned up after it ceases to be valid. | ||||
|      * | ||||
|      * This method accepts an externally generated ID, which should be a UUID | ||||
|      * or similar unique identifier. | ||||
|      * | ||||
|      * @param session | ||||
|      *     The {@link AuthenticationSession} representing the in-progress SAML | ||||
|      *     authentication attempt. | ||||
|      * | ||||
|      * @param identifier | ||||
|      *     A unique and unpredictable string that may be used to represent the | ||||
|      *     given session when calling resume(). | ||||
|      */ | ||||
|     public void defer(AuthenticationSession session, String identifier) { | ||||
|         sessions.put(identifier, session); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Shuts down the executor service that periodically removes all invalid | ||||
|      * authentication sessions. This must be invoked when the SAML extension is | ||||
|      * shut down in order to avoid resource leaks. | ||||
|      */ | ||||
|     public void shutdown() { | ||||
|         executor.shutdownNow(); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,54 +0,0 @@ | ||||
| /* | ||||
|  * 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.saml.acs; | ||||
|  | ||||
| import com.google.common.io.BaseEncoding; | ||||
| import com.google.inject.Singleton; | ||||
| import java.security.SecureRandom; | ||||
|  | ||||
| /** | ||||
|  * Generator of unique and unpredictable identifiers. Each generated identifier | ||||
|  * is an arbitrary, random string produced using a cryptographically-secure | ||||
|  * random number generator and consists of at least 256 bits. | ||||
|  */ | ||||
| @Singleton | ||||
| public class IdentifierGenerator { | ||||
|  | ||||
|     /** | ||||
|      * Cryptographically-secure random number generator for generating unique | ||||
|      * identifiers. | ||||
|      */ | ||||
|     private final SecureRandom secureRandom = new SecureRandom(); | ||||
|  | ||||
|     /** | ||||
|      * Generates a unique and unpredictable identifier. Each identifier is at | ||||
|      * least 256-bit and produced using a cryptographically-secure random | ||||
|      * number generator. | ||||
|      * | ||||
|      * @return | ||||
|      *     A unique and unpredictable identifier. | ||||
|      */ | ||||
|     public String generateIdentifier() { | ||||
|         byte[] bytes = new byte[33]; | ||||
|         secureRandom.nextBytes(bytes); | ||||
|         return BaseEncoding.base64().encode(bytes); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -19,17 +19,12 @@ | ||||
| 
 | ||||
| package org.apache.guacamole.auth.saml.acs; | ||||
| 
 | ||||
| import org.apache.guacamole.auth.sso.AuthenticationSession; | ||||
| 
 | ||||
| /** | ||||
|  * Representation of an in-progress SAML authentication attempt. | ||||
|  */ | ||||
| public class AuthenticationSession { | ||||
| 
 | ||||
|     /** | ||||
|      * The absolute point in time after which this authentication session is | ||||
|      * invalid. This value is a UNIX epoch timestamp, as may be returned by | ||||
|      * {@link System#currentTimeMillis()}. | ||||
|      */ | ||||
|     private final long expirationTimestamp; | ||||
| public class SAMLAuthenticationSession extends AuthenticationSession { | ||||
| 
 | ||||
|     /** | ||||
|      * The request ID of the SAML request associated with the authentication | ||||
| @@ -55,24 +50,21 @@ public class AuthenticationSession { | ||||
|      *     The number of milliseconds that may elapse before this session must | ||||
|      *     be considered invalid. | ||||
|      */ | ||||
|     public AuthenticationSession(String requestId, long expires) { | ||||
|         this.expirationTimestamp = System.currentTimeMillis() + expires; | ||||
|     public SAMLAuthenticationSession(String requestId, long expires) { | ||||
|         super(expires); | ||||
|         this.requestId = requestId; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns whether this authentication session is still valid (has not yet | ||||
|      * expired). If an identity has been asserted by the SAML IdP, this | ||||
|      * {@inheritDoc} | ||||
|      * | ||||
|      * <p>If an identity has been asserted by the SAML IdP, this | ||||
|      * considers also whether the SAML response asserting that identity has | ||||
|      * expired. | ||||
|      * | ||||
|      * @return | ||||
|      *     true if this authentication session is still valid, false if it has | ||||
|      *     expired. | ||||
|      */ | ||||
|     @Override | ||||
|     public boolean isValid() { | ||||
|         return System.currentTimeMillis() < expirationTimestamp | ||||
|                 && (identity == null || identity.isValid()); | ||||
|         return super.isValid() && (identity == null || identity.isValid()); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @@ -0,0 +1,59 @@ | ||||
| /* | ||||
|  * 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.saml.acs; | ||||
|  | ||||
| import com.google.inject.Singleton; | ||||
| import org.apache.guacamole.auth.sso.AuthenticationSessionManager; | ||||
|  | ||||
| /** | ||||
|  * Manager service that temporarily stores SAML authentication attempts while | ||||
|  * the authentication flow is underway. | ||||
|  */ | ||||
| @Singleton | ||||
| public class SAMLAuthenticationSessionManager | ||||
|         extends AuthenticationSessionManager<SAMLAuthenticationSession> { | ||||
|  | ||||
|     /** | ||||
|      * Returns the identity finally asserted by the SAML IdP at the end of the | ||||
|      * authentication process represented by the authentication session with | ||||
|      * the given identifier. If there is no such authentication session, or no | ||||
|      * valid identity has been asserted by the SAML IdP for that session, null | ||||
|      * is returned. | ||||
|      * | ||||
|      * @param identifier | ||||
|      *     The unique string returned by the call to defer(). For convenience, | ||||
|      *     this value may safely be null. | ||||
|      * | ||||
|      * @return | ||||
|      *     The identity finally asserted by the SAML IdP at the end of the | ||||
|      *     authentication process represented by the authentication session | ||||
|      *     with the given identifier, or null if there is no such identity. | ||||
|      */ | ||||
|     public AssertedIdentity getIdentity(String identifier) { | ||||
|  | ||||
|         SAMLAuthenticationSession session = resume(identifier); | ||||
|         if (session != null) | ||||
|             return session.getIdentity(); | ||||
|  | ||||
|         return null; | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -22,7 +22,6 @@ package org.apache.guacamole.auth.saml.acs; | ||||
| import com.google.inject.Inject; | ||||
| import com.google.inject.Singleton; | ||||
| import com.onelogin.saml2.Auth; | ||||
| import com.onelogin.saml2.authn.AuthnRequest; | ||||
| import com.onelogin.saml2.authn.AuthnRequestParams; | ||||
| import com.onelogin.saml2.authn.SamlResponse; | ||||
| import com.onelogin.saml2.exception.SettingsException; | ||||
| @@ -37,6 +36,7 @@ import org.apache.guacamole.GuacamoleException; | ||||
| import org.apache.guacamole.GuacamoleSecurityException; | ||||
| import org.apache.guacamole.GuacamoleServerException; | ||||
| import org.apache.guacamole.auth.saml.conf.ConfigurationService; | ||||
| import org.apache.guacamole.auth.sso.IdentifierGenerator; | ||||
| import org.xml.sax.SAXException; | ||||
|  | ||||
| /** | ||||
| @@ -56,7 +56,7 @@ public class SAMLService { | ||||
|      * Manager of active SAML authentication attempts. | ||||
|      */ | ||||
|     @Inject | ||||
|     private AuthenticationSessionManager sessionManager; | ||||
|     private SAMLAuthenticationSessionManager sessionManager; | ||||
|  | ||||
|     /** | ||||
|      * Generator of arbitrary, unique, unpredictable identifiers. | ||||
| @@ -99,7 +99,7 @@ public class SAMLService { | ||||
|  | ||||
|             // Create a new authentication session to represent this attempt while | ||||
|             // it is in progress, using the request ID that was just issued | ||||
|             AuthenticationSession session = new AuthenticationSession( | ||||
|             SAMLAuthenticationSession session = new SAMLAuthenticationSession( | ||||
|                     auth.getLastRequestId(), | ||||
|                     confService.getAuthenticationTimeout() * 60000L); | ||||
|  | ||||
| @@ -127,7 +127,7 @@ public class SAMLService { | ||||
|  | ||||
|     /** | ||||
|      * Processes the given SAML response, as received by the SAML ACS endpoint | ||||
|      * at the given URL, producing an {@link AuthenticationSession} that now | ||||
|      * at the given URL, producing an {@link SAMLAuthenticationSession} that now | ||||
|      * includes a valid assertion of the user's identity. If the SAML response | ||||
|      * is invalid in any way, an exception is thrown. | ||||
|      * | ||||
| @@ -148,7 +148,7 @@ public class SAMLService { | ||||
|      *     given URL. | ||||
|      * | ||||
|      * @return | ||||
|      *     The {@link AuthenticationSession} associated with the in-progress | ||||
|      *     The {@link SAMLAuthenticationSession} associated with the in-progress | ||||
|      *     authentication attempt, now associated with the {@link AssertedIdentity} | ||||
|      *     representing the identity of the user asserted by the SAML IdP. | ||||
|      * | ||||
| @@ -157,14 +157,14 @@ public class SAMLService { | ||||
|      *     information required to validate or decrypt the response cannot be | ||||
|      *     read. | ||||
|      */ | ||||
|     public AuthenticationSession processResponse(String url, String relayState, | ||||
|     public SAMLAuthenticationSession processResponse(String url, String relayState, | ||||
|             String encodedResponse) throws GuacamoleException { | ||||
|  | ||||
|         if (relayState == null) | ||||
|             throw new GuacamoleSecurityException("\"RelayState\" value " | ||||
|                     + "is missing from SAML response."); | ||||
|  | ||||
|         AuthenticationSession session = sessionManager.resume(relayState); | ||||
|         SAMLAuthenticationSession session = sessionManager.resume(relayState); | ||||
|         if (session == null) | ||||
|             throw new GuacamoleSecurityException("\"RelayState\" value " | ||||
|                     + "included with SAML response is not valid."); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user