mirror of
				https://github.com/gyurix1968/guacamole-client.git
				synced 2025-10-31 00:53:21 +00:00 
			
		
		
		
	GUACAMOLE-1289: Move AuthenticationSession components to guacamole-exit.
This commit is contained in:
		
				
					committed by
					
						 Alex Leitner
						Alex Leitner
					
				
			
			
				
	
			
			
			
						parent
						
							5a135f3361
						
					
				
				
					commit
					13494baa4a
				
			| @@ -1,58 +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.sso; | ||||
|  | ||||
| /** | ||||
|  * Representation of an in-progress 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; | ||||
|  | ||||
|     /** | ||||
|      * Creates a new AuthenticationSession representing an in-progress | ||||
|      * authentication attempt. | ||||
|      * | ||||
|      * @param expires | ||||
|      *     The number of milliseconds that may elapse before this session must | ||||
|      *     be considered invalid. | ||||
|      */ | ||||
|     public AuthenticationSession(long expires) { | ||||
|         this.expirationTimestamp = System.currentTimeMillis() + expires; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns whether this authentication session is still valid (has not yet | ||||
|      * expired). | ||||
|      * | ||||
|      * @return | ||||
|      *     true if this authentication session is still valid, false if it has | ||||
|      *     expired. | ||||
|      */ | ||||
|     public boolean isValid() { | ||||
|         return System.currentTimeMillis() < expirationTimestamp; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,231 +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.sso; | ||||
|  | ||||
| import com.google.inject.Inject; | ||||
| import com.google.inject.Singleton; | ||||
|  | ||||
| import java.util.Map; | ||||
| 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 a user's authentication status while | ||||
|  * the authentication flow is underway. Authentication attempts are represented | ||||
|  * as temporary authentication sessions, allowing authentication attempts to | ||||
|  * span multiple requests, redirects, etc. Invalid or stale authentication | ||||
|  * sessions are automatically purged from storage. | ||||
|  * | ||||
|  * @param <T> | ||||
|  *     The type of sessions managed by this session manager. | ||||
|  */ | ||||
| @Singleton | ||||
| public class AuthenticationSessionManager<T extends AuthenticationSession> { | ||||
|  | ||||
|     /** | ||||
|      * Generator of arbitrary, unique, unpredictable identifiers. | ||||
|      */ | ||||
|     @Inject | ||||
|     private IdentifierGenerator idGenerator; | ||||
|  | ||||
|     /** | ||||
|      * Map of authentication session identifiers to their associated | ||||
|      * {@link AuthenticationSession}. | ||||
|      */ | ||||
|     private final ConcurrentMap<String, T> sessions = new ConcurrentHashMap<>(); | ||||
|  | ||||
|     /** | ||||
|      * Set of identifiers of all sessions that are in a pending state, meaning | ||||
|      * that the session was successfully created, but the overall auth result | ||||
|      * has not yet been determined. | ||||
|      * | ||||
|      * Exposed as a ConcurrentMap instead of a Set because there is no | ||||
|      * ConcurrentSet class offering the required atomic operations. | ||||
|      */ | ||||
|     private final ConcurrentMap<String, Boolean> pendingSessions = 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 | ||||
|      * authentication attempts. Invalid, stale sessions are automatically | ||||
|      * cleaned up. | ||||
|      */ | ||||
|     public AuthenticationSessionManager() { | ||||
|         executor.scheduleAtFixedRate(() -> { | ||||
|  | ||||
|             // Invalidate any stale sessions | ||||
|             for (Map.Entry<String, T> entry : sessions.entrySet()) { | ||||
|                 if (!entry.getValue().isValid())  | ||||
|                     invalidateSession(entry.getKey()); | ||||
|             } | ||||
|  | ||||
|         }, 1, 1, TimeUnit.MINUTES); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Generates a cryptographically-secure value identical in form to the | ||||
|      * session tokens generated by {@link #defer(org.apache.guacamole.auth.sso.AuthenticationSession)} | ||||
|      * but invalid. The returned value is indistinguishable from a valid token, | ||||
|      * but is not a valid token. | ||||
|      * | ||||
|      * @return | ||||
|      *     An invalid token value that is indistinguishable from a valid | ||||
|      *     token. | ||||
|      */ | ||||
|     public String generateInvalid() { | ||||
|         return idGenerator.generateIdentifier(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Remove the session associated with the given identifier, if any, from the | ||||
|      * map of sessions, and the set of pending sessions. | ||||
|      * | ||||
|      * @param identifier | ||||
|      *     The identifier of the session to remove, if one exists. | ||||
|      */ | ||||
|     public void invalidateSession(String identifier) { | ||||
|  | ||||
|         // Do not attempt to remove a null identifier | ||||
|         if (identifier == null) | ||||
|             return; | ||||
|  | ||||
|         // Remove from the overall list of sessions | ||||
|         sessions.remove(identifier); | ||||
|  | ||||
|         // Remove from the set of pending sessions | ||||
|         pendingSessions.remove(identifier); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reactivate (remove from pending) the session associated with the given | ||||
|      * session identifier, if any. After calling this method, any session with | ||||
|      * the given identifier will be ready to be resumed again. | ||||
|      *  | ||||
|      * @param identifier | ||||
|      *     The identifier of the session to reactivate, if one exists. | ||||
|      */ | ||||
|     public void reactivateSession(String identifier) { | ||||
|  | ||||
|         // Remove from the set of pending sessions to reactivate | ||||
|         if (identifier != null) | ||||
|             pendingSessions.remove(identifier); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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 T resume(String identifier) { | ||||
|         if (identifier != null) { | ||||
|  | ||||
|             T session = sessions.get(identifier); | ||||
|  | ||||
|             // Mark the session as pending. NOTE: Unless explicitly removed | ||||
|             // from pending status via a call to reactivateSession(), | ||||
|             // the next attempt to resume this session will fail | ||||
|             if (pendingSessions.putIfAbsent(identifier, true) != null) { | ||||
|  | ||||
|                 // If the session was already marked as pending, invalidate it | ||||
|                 invalidateSession(identifier); | ||||
|                 return null; | ||||
|  | ||||
|             } | ||||
|  | ||||
|             if (session != null && session.isValid()) | ||||
|                 return session; | ||||
|         } | ||||
|  | ||||
|         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 | ||||
|      *     authentication attempt. | ||||
|      * | ||||
|      * @return | ||||
|      *     A unique and unpredictable string that may be used to represent the | ||||
|      *     given session when calling resume(). | ||||
|      */ | ||||
|     public String defer(T 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 | ||||
|      *     authentication attempt. | ||||
|      * | ||||
|      * @param identifier | ||||
|      *     A unique and unpredictable string that may be used to represent the | ||||
|      *     given session when calling resume(). | ||||
|      */ | ||||
|     public void defer(T 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 auth extension is | ||||
|      * shut down in order to avoid resource leaks. | ||||
|      */ | ||||
|     public void shutdown() { | ||||
|         executor.shutdownNow(); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,106 +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.sso; | ||||
|  | ||||
| import com.google.common.io.BaseEncoding; | ||||
| import com.google.inject.Singleton; | ||||
| import java.math.BigInteger; | ||||
| 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. | ||||
|  */ | ||||
| @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. The identifier may contain characters that differ only | ||||
|      * in case. | ||||
|      * | ||||
|      * @return | ||||
|      *     A unique and unpredictable identifier with at least 256 bits of | ||||
|      *     entropy. | ||||
|      */ | ||||
|     public String generateIdentifier() { | ||||
|         return generateIdentifier(256); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Generates a unique and unpredictable identifier having at least the | ||||
|      * given number of bits of entropy. The resulting identifier may have more | ||||
|      * than the number of bits required. The identifier may contain characters | ||||
|      * that differ only in case. | ||||
|      * | ||||
|      * @param minBits | ||||
|      *     The number of bits of entropy that the identifier should contain. | ||||
|      * | ||||
|      * @return | ||||
|      *     A unique and unpredictable identifier with at least the given number | ||||
|      *     of bits of entropy. | ||||
|      */ | ||||
|     public String generateIdentifier(int minBits) { | ||||
|         return generateIdentifier(minBits, true); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Generates a unique and unpredictable identifier having at least the | ||||
|      * given number of bits of entropy. The resulting identifier may have more | ||||
|      * than the number of bits required. The identifier may contain characters | ||||
|      * that differ only in case. | ||||
|      * | ||||
|      * @param minBits | ||||
|      *     The number of bits of entropy that the identifier should contain. | ||||
|      * | ||||
|      * @param caseSensitive | ||||
|      *     Whether identifiers are permitted to contain characters that vary | ||||
|      *     by case. If false, all characters that may vary by case will be | ||||
|      *     lowercase, and the generated identifier will be longer. | ||||
|      * | ||||
|      * @return | ||||
|      *     A unique and unpredictable identifier with at least the given number | ||||
|      *     of bits of entropy. | ||||
|      */ | ||||
|     public String generateIdentifier(int minBits, boolean caseSensitive) { | ||||
|  | ||||
|         // Generate a base64 identifier if we're allowed to vary by case | ||||
|         if (caseSensitive) { | ||||
|             int minBytes = (minBits + 23) / 24 * 3; // Round up to nearest multiple of 3 bytes, as base64 encodes blocks of 3 bytes at a time | ||||
|             byte[] bytes = new byte[minBytes]; | ||||
|             secureRandom.nextBytes(bytes); | ||||
|             return BaseEncoding.base64().encode(bytes); | ||||
|         } | ||||
|  | ||||
|         // Generate base32 identifiers if we cannot vary by case | ||||
|         minBits = (minBits + 4) / 5 * 5; // Round up to nearest multiple of 5 bits, as base32 encodes 5 bits at a time | ||||
|         return new BigInteger(minBits, secureRandom).toString(32); | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -19,11 +19,11 @@ | ||||
|  | ||||
| package org.apache.guacamole.auth.sso; | ||||
|  | ||||
| import com.google.inject.Inject; | ||||
| import java.util.Iterator; | ||||
| import java.util.Locale; | ||||
| import java.util.Map; | ||||
| import java.util.concurrent.ConcurrentHashMap; | ||||
| import org.apache.guacamole.net.auth.IdentifierGenerator; | ||||
|  | ||||
| /** | ||||
|  * Service for generating and validating single-use random tokens (nonces). | ||||
| @@ -31,12 +31,6 @@ import java.util.concurrent.ConcurrentHashMap; | ||||
|  */ | ||||
| public class NonceService { | ||||
|  | ||||
|     /** | ||||
|      * Generator of arbitrary, unique, unpredictable identifiers. | ||||
|      */ | ||||
|     @Inject | ||||
|     private IdentifierGenerator idGenerator; | ||||
|  | ||||
|     /** | ||||
|      * Map of all generated nonces to their corresponding expiration timestamps. | ||||
|      * This Map must be periodically swept of expired nonces to avoid growing | ||||
| @@ -107,7 +101,7 @@ public class NonceService { | ||||
|         sweepExpiredNonces(); | ||||
|  | ||||
|         // Generate and store nonce, along with expiration timestamp | ||||
|         String nonce = idGenerator.generateIdentifier(NONCE_BITS, false); | ||||
|         String nonce = IdentifierGenerator.generateIdentifier(NONCE_BITS, false); | ||||
|         nonces.put(nonce, System.currentTimeMillis() + maxAge); | ||||
|         return nonce; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user