GUACAMOLE-103: Exception handling, token, and SAMLResponseMap updates.

Exception handling within the SAML extension has been updated such that
exceptions generate a Guacamole Error rather than a username/password
login.  Tokens now are canonicalized with a standard prefix.  Now using
an Iterator to handle SAMLResponseMap cleanup.
diff --git a/extensions/guacamole-auth-saml/src/main/java/org/apache/guacamole/auth/saml/AuthenticationProviderService.java b/extensions/guacamole-auth-saml/src/main/java/org/apache/guacamole/auth/saml/AuthenticationProviderService.java
index b7cde82..b256e9b 100644
--- a/extensions/guacamole-auth-saml/src/main/java/org/apache/guacamole/auth/saml/AuthenticationProviderService.java
+++ b/extensions/guacamole-auth-saml/src/main/java/org/apache/guacamole/auth/saml/AuthenticationProviderService.java
@@ -44,14 +44,15 @@
 import org.apache.guacamole.auth.saml.conf.ConfigurationService;
 import org.apache.guacamole.auth.saml.user.SAMLAuthenticatedUser;
 import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleServerException;
 import org.apache.guacamole.form.Field;
 import org.apache.guacamole.form.RedirectField;
 import org.apache.guacamole.language.TranslatableMessage;
 import org.apache.guacamole.net.auth.AuthenticatedUser;
 import org.apache.guacamole.net.auth.Credentials;
 import org.apache.guacamole.net.auth.credentials.CredentialsInfo;
-import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException;
 import org.apache.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException;
+import org.apache.guacamole.token.TokenName;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.xml.sax.SAXException;
@@ -64,7 +65,7 @@
     /**
      * Logger for this class.
      */
-    private final Logger logger = LoggerFactory.getLogger(AuthenticationProviderService.class);
+    private static final Logger logger = LoggerFactory.getLogger(AuthenticationProviderService.class);
 
     /**
      * Service for retrieving SAML configuration information.
@@ -83,6 +84,8 @@
      */
     @Inject
     private SAMLResponseMap samlResponseMap;
+    
+    private static final String SAML_ATTRIBUTE_TOKEN_PREFIX = "SAML_";
 
     /**
      * Returns an AuthenticatedUser representing the user authenticated by the
@@ -119,9 +122,8 @@
                     // Generate the response object
                     if (!samlResponseMap.hasSamlResponse(responseHash)) {
                         logger.warn("SAML response was not found.");
-                        logger.debug("SAML response hash {} not fonud in response map.", responseHash);
-                        throw new GuacamoleInvalidCredentialsException("Provided response was not found.",
-                                CredentialsInfo.USERNAME_PASSWORD);
+                        logger.debug("SAML response hash {} not found in response map.", responseHash);
+                        throw new GuacamoleServerException("Provided response was not found in response map.");
                     }
                         
                     SamlResponse samlResponse = samlResponseMap.getSamlResponse(responseHash);
@@ -129,8 +131,7 @@
                     if (!samlResponse.validateNumAssertions()) {
                         logger.warn("SAML response contained other than single assertion.");
                         logger.debug("validateNumAssertions returned false.");
-                        throw new GuacamoleInvalidCredentialsException("Error during SAML login.",
-                                CredentialsInfo.USERNAME_PASSWORD);
+                        throw new GuacamoleServerException("Unable to validate SAML assertions.");
                     }
                     
                     // Validate timestamps, generating ValidationException if this fails.
@@ -159,48 +160,41 @@
                     }
                 }
 
-                // Errors are logged and result in a normal username/password login box.
+                // Catch errors and convert to a GuacamoleExcetion.
                 catch (IOException e) {
                     logger.warn("Error during I/O while parsing SAML response: {}", e.getMessage());
                     logger.debug("Received IOException when trying to parse SAML response.", e);
-                    throw new GuacamoleInvalidCredentialsException("Error during SAML login.",
-                                CredentialsInfo.USERNAME_PASSWORD);
+                    throw new GuacamoleServerException("IOException received while processing SAML response.", e);
                 }
                 catch (ParserConfigurationException e) {
                     logger.warn("Error configuring XML parser: {}", e.getMessage());
                     logger.debug("Received ParserConfigurationException when trying to parse SAML response.", e);
-                    throw new GuacamoleInvalidCredentialsException("Error during SAML login.",
-                                CredentialsInfo.USERNAME_PASSWORD);
+                    throw new GuacamoleServerException("XML ParserConfigurationException received while processing SAML response.", e);
                 }
                 catch (SAXException e) {
                     logger.warn("Bad XML when parsing SAML response: {}", e.getMessage());
                     logger.debug("Received SAXException while parsing SAML response.", e);
-                    throw new GuacamoleInvalidCredentialsException("Error during SAML login.",
-                                CredentialsInfo.USERNAME_PASSWORD);
+                    throw new GuacamoleServerException("XML SAXException received while processing SAML response.", e);
                 }
                 catch (SettingsException e) {
                     logger.warn("Error with SAML settings while parsing response: {}", e.getMessage());
                     logger.debug("Received SettingsException while parsing SAML response.", e);
-                    throw new GuacamoleInvalidCredentialsException("Error during SAML login.",
-                                CredentialsInfo.USERNAME_PASSWORD);
+                    throw new GuacamoleServerException("SAML SettingsException received while process SAML response.", e);
                 }
                 catch (ValidationError e) {
                     logger.warn("Error validating SAML response: {}", e.getMessage());
                     logger.debug("Received ValidationError while parsing SAML response.", e);
-                    throw new GuacamoleInvalidCredentialsException("Error during SAML login.",
-                                CredentialsInfo.USERNAME_PASSWORD);
+                    throw new GuacamoleServerException("SAML ValidationError received while processing SAML response.", e);
                 }
                 catch (XPathExpressionException e) {
                     logger.warn("Problem with XML parsing response: {}", e.getMessage());
                     logger.debug("Received XPathExpressionException while processing SAML response.", e);
-                    throw new GuacamoleInvalidCredentialsException("Error during SAML login.",
-                                CredentialsInfo.USERNAME_PASSWORD);
+                    throw new GuacamoleServerException("XML XPathExpressionExcetion received while processing SAML response.", e);
                 }
                 catch (Exception e) {
                     logger.warn("Exception while getting name from SAML response: {}", e.getMessage());
                     logger.debug("Received Exception while retrieving name from SAML response.", e);
-                    throw new GuacamoleInvalidCredentialsException("Error during SAML login.",
-                                CredentialsInfo.USERNAME_PASSWORD);
+                    throw new GuacamoleServerException("Generic Exception received processing SAML response.", e);
                 }
             }
         }
@@ -215,14 +209,12 @@
         catch (IOException e) {
             logger.error("Error encoding authentication request to string: {}", e.getMessage());
             logger.debug("Got IOException encoding authentication request.", e);
-            throw new GuacamoleInvalidCredentialsException("Error during SAML login.",
-                    CredentialsInfo.USERNAME_PASSWORD);
+            throw new GuacamoleServerException("IOException received while generating SAML authentication URI.", e);
         }
         catch(URISyntaxException e) {
             logger.error("Error generating URI for authentication redirect: {}", e.getMessage());
             logger.debug("Got URISyntaxException generating authentication URI", e);
-            throw new GuacamoleInvalidCredentialsException("Error during SAML login.",
-                    CredentialsInfo.USERNAME_PASSWORD);
+            throw new GuacamoleServerException("URISyntaxException received while generating SAML authentication URI.", e);
         }
 
         // Redirect to SAML Identity Provider (IdP)
@@ -254,7 +246,9 @@
         for (Entry<String, List<String>> entry : attributes.entrySet()) {
             
             List<String> values = entry.getValue();
-            tokens.put(entry.getKey(), values.get(0));
+            tokens.put(TokenName.canonicalize(
+                    entry.getKey(), SAML_ATTRIBUTE_TOKEN_PREFIX),
+                    values.get(0));
             
         }
         
diff --git a/extensions/guacamole-auth-saml/src/main/java/org/apache/guacamole/auth/saml/SAMLResponseMap.java b/extensions/guacamole-auth-saml/src/main/java/org/apache/guacamole/auth/saml/SAMLResponseMap.java
index 392b8a1..9010996 100644
--- a/extensions/guacamole-auth-saml/src/main/java/org/apache/guacamole/auth/saml/SAMLResponseMap.java
+++ b/extensions/guacamole-auth-saml/src/main/java/org/apache/guacamole/auth/saml/SAMLResponseMap.java
@@ -23,6 +23,7 @@
 import com.onelogin.saml2.authn.SamlResponse;
 import com.onelogin.saml2.exception.ValidationError;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.Executors;
@@ -107,13 +108,13 @@
         public void run() {
 
             // Loop through responses in map and remove ones that are no longer valid.
-            Collection<SamlResponse> samlResponses = samlResponseMap.values();
-            for (SamlResponse value : samlResponses) {
+            Iterator<SamlResponse> responseIterator = samlResponseMap.values().iterator();
+            while (responseIterator.hasNext()) {
                 try {
-                    value.validateTimestamps();
+                    responseIterator.next().validateTimestamps();
                 }
                 catch (ValidationError e) {
-                    samlResponses.remove(value);
+                    responseIterator.remove();
                 }
             }