Add missing handlers for default protocol/parameter.
[guacamole.git] / src / main / java / net / sourceforge / guacamole / net / basic / BasicFileAuthenticationProvider.java
index 44ff293..f520959 100644 (file)
@@ -19,38 +19,59 @@ package net.sourceforge.guacamole.net.basic;
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+import java.io.BufferedReader;
 import net.sourceforge.guacamole.net.auth.AuthenticationProvider;
 import java.io.File;
+import java.io.FileReader;
 import java.io.IOException;
+import java.io.Reader;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import net.sourceforge.guacamole.GuacamoleException;
-import net.sourceforge.guacamole.net.auth.UsernamePassword;
-import net.sourceforge.guacamole.net.basic.properties.BasicGuacamoleProperties;
+import net.sourceforge.guacamole.net.auth.Credentials;
+import net.sourceforge.guacamole.properties.FileGuacamoleProperty;
 import net.sourceforge.guacamole.properties.GuacamoleProperties;
 import net.sourceforge.guacamole.protocol.GuacamoleConfiguration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
 import org.xml.sax.XMLReader;
 import org.xml.sax.helpers.DefaultHandler;
 import org.xml.sax.helpers.XMLReaderFactory;
 
-public class BasicFileAuthenticationProvider implements AuthenticationProvider<UsernamePassword> {
+/**
+ * Authenticates users against a static list of username/password pairs.
+ * Each username/password may be associated with multiple configurations.
+ * This list is stored in an XML file which is reread if modified.
+ * 
+ * @author Michael Jumper, Michal Kotas
+ */
+public class BasicFileAuthenticationProvider implements AuthenticationProvider {
 
     private Logger logger = LoggerFactory.getLogger(BasicFileAuthenticationProvider.class);
     
     private long mappingTime;
     private Map<String, AuthInfo> mapping;
 
+    /**
+     * The filename of the XML file to read the user mapping from.
+     */
+    public static final FileGuacamoleProperty BASIC_USER_MAPPING = new FileGuacamoleProperty() {
+
+        @Override
+        public String getName() { return "basic-user-mapping"; }
+
+    };
+
     private File getUserMappingFile() throws GuacamoleException {
 
         // Get user mapping file
-        return GuacamoleProperties.getProperty(BasicGuacamoleProperties.BASIC_USER_MAPPING);
+        return GuacamoleProperties.getProperty(BASIC_USER_MAPPING);
 
     }
 
@@ -66,12 +87,18 @@ public class BasicFileAuthenticationProvider implements AuthenticationProvider<U
         // Parse document
         try {
 
+            // Set up parser
             BasicUserMappingContentHandler contentHandler = new BasicUserMappingContentHandler();
 
             XMLReader parser = XMLReaderFactory.createXMLReader();
             parser.setContentHandler(contentHandler);
-            parser.parse(mapFile.getAbsolutePath());
 
+            // Read and parse file
+            Reader reader = new BufferedReader(new FileReader(mapFile));
+            parser.parse(new InputSource(reader));
+            reader.close();
+
+            // Init mapping and record mod time of file
             mappingTime = mapFile.lastModified();
             mapping = contentHandler.getUserMapping();
 
@@ -86,7 +113,7 @@ public class BasicFileAuthenticationProvider implements AuthenticationProvider<U
     }
 
     @Override
-    public Map<String, GuacamoleConfiguration> getAuthorizedConfigurations(UsernamePassword credentials) throws GuacamoleException {
+    public Map<String, GuacamoleConfiguration> getAuthorizedConfigurations(Credentials credentials) throws GuacamoleException {
 
         // Check mapping file mod time
         File userMappingFile = getUserMappingFile();
@@ -106,14 +133,13 @@ public class BasicFileAuthenticationProvider implements AuthenticationProvider<U
         if (mapping == null)
             throw new GuacamoleException("User mapping could not be read.");
         
-        Map<String, GuacamoleConfiguration> configs = new HashMap<String, GuacamoleConfiguration>();
-        
         // Validate and return info for given user and pass
         AuthInfo info = mapping.get(credentials.getUsername());
         if (info != null && info.validate(credentials.getUsername(), credentials.getPassword()))
-            configs.put("DEFAULT", info.getConfiguration());
+            return info.getConfigurations();
 
-        return configs;
+        // Unauthorized
+        return null;
 
     }
 
@@ -128,14 +154,14 @@ public class BasicFileAuthenticationProvider implements AuthenticationProvider<U
         private String auth_password;
         private Encoding auth_encoding;
 
-        private GuacamoleConfiguration config;
+        private Map<String, GuacamoleConfiguration> configs;
 
         public AuthInfo(String auth_username, String auth_password, Encoding auth_encoding) {
             this.auth_username = auth_username;
             this.auth_password = auth_password;
             this.auth_encoding = auth_encoding;
 
-            config = new GuacamoleConfiguration();
+            configs = new HashMap<String, GuacamoleConfiguration>();
         }
 
         private static final char HEX_CHARS[] = {
@@ -191,12 +217,24 @@ public class BasicFileAuthenticationProvider implements AuthenticationProvider<U
 
         }
 
-        public GuacamoleConfiguration getConfiguration() {
+        public GuacamoleConfiguration getConfiguration(String name) {
+
+            // Create new configuration if not already in map
+            GuacamoleConfiguration config = configs.get(name);
+            if (config == null) {
+                config = new GuacamoleConfiguration();
+                configs.put(name, config);
+            }
+
             return config;
+
         }
 
-    }
+        public Map<String, GuacamoleConfiguration> getConfigurations() {
+            return configs;
+        }
 
+    }
 
     private static class BasicUserMappingContentHandler extends DefaultHandler {
 
@@ -209,15 +247,26 @@ public class BasicFileAuthenticationProvider implements AuthenticationProvider<U
         private enum State {
             ROOT,
             USER_MAPPING,
+
+            /* Username/password pair */
             AUTH_INFO,
+
+            /* Connection configuration information */
+            CONNECTION,
             PROTOCOL,
             PARAMETER,
+
+            /* Configuration information associated with default connection */
+            DEFAULT_CONNECTION_PROTOCOL,
+            DEFAULT_CONNECTION_PARAMETER,
+
             END;
         }
 
         private State state = State.ROOT;
         private AuthInfo current = null;
         private String currentParameter = null;
+        private String currentConnection = null;
 
         @Override
         public void endElement(String uri, String localName, String qName) throws SAXException {
@@ -248,11 +297,20 @@ public class BasicFileAuthenticationProvider implements AuthenticationProvider<U
                     }
 
                     break;
+                    
+                case CONNECTION:
+
+                    if (localName.equals("connection")) {
+                        state = State.AUTH_INFO;
+                        return;
+                    }
+
+                    break;                
 
                 case PROTOCOL:
 
                     if (localName.equals("protocol")) {
-                        state = State.AUTH_INFO;
+                        state = State.CONNECTION;
                         return;
                     }
 
@@ -261,6 +319,24 @@ public class BasicFileAuthenticationProvider implements AuthenticationProvider<U
                 case PARAMETER:
 
                     if (localName.equals("param")) {
+                        state = State.CONNECTION;
+                        return;
+                    }
+
+                    break;
+
+                case DEFAULT_CONNECTION_PROTOCOL:
+
+                    if (localName.equals("protocol")) {
+                        state = State.AUTH_INFO;
+                        return;
+                    }
+
+                    break;
+
+                case DEFAULT_CONNECTION_PARAMETER:
+
+                    if (localName.equals("param")) {
                         state = State.AUTH_INFO;
                         return;
                     }
@@ -288,7 +364,6 @@ public class BasicFileAuthenticationProvider implements AuthenticationProvider<U
 
                     break;
 
-                // Only <authorize> tags allowed in main document
                 case USER_MAPPING:
 
                     if (localName.equals("authorize")) {
@@ -320,6 +395,45 @@ public class BasicFileAuthenticationProvider implements AuthenticationProvider<U
 
                 case AUTH_INFO:
 
+                    if (localName.equals("connection")) {
+
+                        currentConnection = attributes.getValue("name");
+                        if (currentConnection == null)
+                            throw new SAXException("Attribute \"name\" required for connection tag.");
+                        
+                        // Next state
+                        state = State.CONNECTION;
+                        return;
+                    }
+
+                    if (localName.equals("protocol")) {
+
+                        // Associate protocol with default connection
+                        currentConnection = "DEFAULT";
+                        
+                        // Next state
+                        state = State.DEFAULT_CONNECTION_PROTOCOL;
+                        return;
+                    }
+
+                    if (localName.equals("param")) {
+
+                        // Associate parameter with default connection
+                        currentConnection = "DEFAULT";
+                        
+                        currentParameter = attributes.getValue("name");
+                        if (currentParameter == null)
+                            throw new SAXException("Attribute \"name\" required for param tag.");
+
+                        // Next state
+                        state = State.DEFAULT_CONNECTION_PARAMETER;
+                        return;
+                    }
+
+                    break;
+                    
+                case CONNECTION:
+
                     if (localName.equals("protocol")) {
                         // Next state
                         state = State.PROTOCOL;
@@ -337,7 +451,7 @@ public class BasicFileAuthenticationProvider implements AuthenticationProvider<U
                         return;
                     }
 
-                    break;
+                    break;                   
 
             }
 
@@ -349,15 +463,20 @@ public class BasicFileAuthenticationProvider implements AuthenticationProvider<U
         public void characters(char[] ch, int start, int length) throws SAXException {
 
             String str = new String(ch, start, length);
+   
             switch (state) {
 
                 case PROTOCOL:
-                    current.getConfiguration()
-                            .setProtocol(str);
+                case DEFAULT_CONNECTION_PROTOCOL:
+
+                    current.getConfiguration(currentConnection)
+                        .setProtocol(str);
                     return;
 
                 case PARAMETER:
-                    current.getConfiguration()
+                case DEFAULT_CONNECTION_PARAMETER:
+
+                    current.getConfiguration(currentConnection)
                             .setParameter(currentParameter, str);
                     return;