Migrated to new tunnel servlet API, updated .gitignore
authorMichael Jumper <zhangmaike@users.sourceforge.net>
Sun, 23 Jan 2011 23:25:37 +0000 (15:25 -0800)
committerMichael Jumper <zhangmaike@users.sourceforge.net>
Sun, 23 Jan 2011 23:25:37 +0000 (15:25 -0800)
.gitignore
src/main/java/net/sourceforge/guacamole/net/authentication/basic/BasicFileAuthenticationProvider.java [deleted file]
src/main/java/net/sourceforge/guacamole/net/authentication/basic/BasicGuacamoleClientProvider.java [deleted file]
src/main/java/net/sourceforge/guacamole/net/authentication/basic/BasicLogin.java [deleted file]
src/main/java/net/sourceforge/guacamole/net/basic/BasicFileAuthenticationProvider.java [new file with mode: 0644]
src/main/java/net/sourceforge/guacamole/net/basic/BasicGuacamoleTunnelServlet.java [new file with mode: 0644]
src/main/java/net/sourceforge/guacamole/net/basic/BasicLogin.java [new file with mode: 0644]
src/main/webapp/WEB-INF/web.xml
src/main/webapp/index.html

index 2f7896d..42f4a1a 100644 (file)
@@ -1 +1,2 @@
 target/
+*~
diff --git a/src/main/java/net/sourceforge/guacamole/net/authentication/basic/BasicFileAuthenticationProvider.java b/src/main/java/net/sourceforge/guacamole/net/authentication/basic/BasicFileAuthenticationProvider.java
deleted file mode 100644 (file)
index 380d7ed..0000000
+++ /dev/null
@@ -1,359 +0,0 @@
-
-package net.sourceforge.guacamole.net.authentication.basic;
-
-/*
- *  Guacamole - Clientless Remote Desktop
- *  Copyright (C) 2010  Michael Jumper
- *
- *  This program is free software: you can redistribute it and/or modify
- *  it under the terms of the GNU Affero General Public License as published by
- *  the Free Software Foundation, either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU Affero General Public License for more details.
- *
- *  You should have received a copy of the GNU Affero General Public License
- *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-import java.io.File;
-import java.io.IOException;
-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.Configuration;
-import net.sourceforge.guacamole.net.GuacamoleProperties;
-import org.xml.sax.Attributes;
-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 BasicLogin.AuthenticationProvider {
-
-    private long mappingTime;
-    private Map<String, AuthInfo> mapping;
-
-    private File getUserMappingFile() throws GuacamoleException {
-
-        // Get user mapping filename
-        String filename = GuacamoleProperties.getProperty("basic-user-mapping");
-        if (filename == null)
-            return null;
-
-        return new File(filename);
-
-    }
-
-    public synchronized void init() throws GuacamoleException {
-
-        // Get user mapping file
-        File mapFile = getUserMappingFile();
-        if (mapFile == null)
-            throw new GuacamoleException("Missing \"basic-user-mapping\" parameter required for basic login.");
-
-        // Parse document
-        try {
-
-            BasicUserMappingContentHandler contentHandler = new BasicUserMappingContentHandler();
-
-            XMLReader parser = XMLReaderFactory.createXMLReader();
-            parser.setContentHandler(contentHandler);
-            parser.parse(mapFile.getAbsolutePath());
-
-            mappingTime = mapFile.lastModified();
-            mapping = contentHandler.getUserMapping();
-
-        }
-        catch (IOException e) {
-            throw new GuacamoleException("Error reading basic user mapping file.", e);
-        }
-        catch (SAXException e) {
-            throw new GuacamoleException("Error parsing basic user mapping XML.", e);
-        }
-
-    }
-
-    @Override
-    public Configuration getAuthorizedConfiguration(String username, String password) throws GuacamoleException {
-
-        // Check mapping file mod time
-        File userMappingFile = getUserMappingFile();
-        if (userMappingFile.exists() && mappingTime < userMappingFile.lastModified()) {
-
-            // If modified recently, gain exclusive access and recheck
-            synchronized (this) {
-                if (userMappingFile.exists() && mappingTime < userMappingFile.lastModified())
-                    init(); // If still not up to date, re-init
-            }
-
-        }
-
-        AuthInfo info = mapping.get(username);
-        if (info != null && info.validate(username, password))
-            return info.getConfiguration();
-
-        return null;
-
-    }
-
-    public static class AuthInfo {
-
-        public static enum Encoding {
-            PLAIN_TEXT,
-            MD5
-        }
-
-        private String auth_username;
-        private String auth_password;
-        private Encoding auth_encoding;
-
-        private Configuration config;
-
-        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 Configuration();
-        }
-
-        private static final char HEX_CHARS[] = {
-            '0', '1', '2', '3', '4', '5', '6', '7',
-            '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
-        };
-
-        public static String getHexString(byte[] bytes) {
-
-            if (bytes == null)
-                return null;
-
-            StringBuilder hex = new StringBuilder(2 * bytes.length);
-            for (byte b : bytes) {
-                hex.append(HEX_CHARS[(b & 0xF0) >> 4])
-                   .append(HEX_CHARS[(b & 0x0F)     ]);
-            }
-
-            return hex.toString();
-
-        }
-
-
-        public boolean validate(String username, String password) {
-
-            // If username matches
-            if (username != null && password != null && username.equals(auth_username)) {
-
-                switch (auth_encoding) {
-
-                    case PLAIN_TEXT:
-
-                        // Compare plaintext
-                        return password.equals(auth_password);
-
-                    case MD5:
-
-                        // Compare hashed password
-                        try {
-                            MessageDigest digest = MessageDigest.getInstance("MD5");
-                            String hashedPassword = getHexString(digest.digest(password.getBytes()));
-                            return hashedPassword.equals(auth_password.toUpperCase());
-                        }
-                        catch (NoSuchAlgorithmException e) {
-                            throw new UnsupportedOperationException("Unexpected lack of MD5 support.", e);
-                        }
-
-                }
-
-            }
-
-            return false;
-
-        }
-
-        public Configuration getConfiguration() {
-            return config;
-        }
-
-    }
-
-
-    private static class BasicUserMappingContentHandler extends DefaultHandler {
-
-        private Map<String, AuthInfo> authMapping = new HashMap<String, AuthInfo>();
-
-        public Map<String, AuthInfo> getUserMapping() {
-            return Collections.unmodifiableMap(authMapping);
-        }
-
-        private enum State {
-            ROOT,
-            USER_MAPPING,
-            AUTH_INFO,
-            PROTOCOL,
-            PARAMETER,
-            END;
-        }
-
-        private State state = State.ROOT;
-        private AuthInfo current = null;
-        private String currentParameter = null;
-
-        @Override
-        public void endElement(String uri, String localName, String qName) throws SAXException {
-
-            switch (state)  {
-
-                case USER_MAPPING:
-
-                    if (localName.equals("user-mapping")) {
-                        state = State.END;
-                        return;
-                    }
-
-                    break;
-
-                case AUTH_INFO:
-
-                    if (localName.equals("authorize")) {
-
-                        // Finalize mapping for this user
-                        authMapping.put(
-                            current.auth_username,
-                            current
-                        );
-
-                        state = State.USER_MAPPING;
-                        return;
-                    }
-
-                    break;
-
-                case PROTOCOL:
-
-                    if (localName.equals("protocol")) {
-                        state = State.AUTH_INFO;
-                        return;
-                    }
-
-                    break;
-
-                case PARAMETER:
-
-                    if (localName.equals("param")) {
-                        state = State.AUTH_INFO;
-                        return;
-                    }
-
-                    break;
-
-            }
-
-            throw new SAXException("Tag not yet complete: " + localName);
-
-        }
-
-        @Override
-        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
-
-            switch (state)  {
-
-                // Document must be <user-mapping>
-                case ROOT:
-
-                    if (localName.equals("user-mapping")) {
-                        state = State.USER_MAPPING;
-                        return;
-                    }
-
-                    break;
-
-                // Only <authorize> tags allowed in main document
-                case USER_MAPPING:
-
-                    if (localName.equals("authorize")) {
-
-                        AuthInfo.Encoding encoding;
-                        String encodingString = attributes.getValue("encoding");
-                        if (encodingString == null)
-                            encoding = AuthInfo.Encoding.PLAIN_TEXT;
-                        else if (encodingString.equals("plain"))
-                            encoding = AuthInfo.Encoding.PLAIN_TEXT;
-                        else if (encodingString.equals("md5"))
-                            encoding = AuthInfo.Encoding.MD5;
-                        else
-                            throw new SAXException("Invalid encoding type");
-
-
-                        current = new AuthInfo(
-                            attributes.getValue("username"),
-                            attributes.getValue("password"),
-                            encoding
-                        );
-
-                        // Next state
-                        state = State.AUTH_INFO;
-                        return;
-                    }
-
-                    break;
-
-                case AUTH_INFO:
-
-                    if (localName.equals("protocol")) {
-                        // Next state
-                        state = State.PROTOCOL;
-                        return;
-                    }
-
-                    if (localName.equals("param")) {
-
-                        currentParameter = attributes.getValue("name");
-                        if (currentParameter == null)
-                            throw new SAXException("Attribute \"name\" required for param tag.");
-
-                        // Next state
-                        state = State.PARAMETER;
-                        return;
-                    }
-
-                    break;
-
-            }
-
-            throw new SAXException("Unexpected tag: " + localName);
-
-        }
-
-        @Override
-        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);
-                    return;
-
-                case PARAMETER:
-                    current.getConfiguration().setParameter(currentParameter, str);
-                    return;
-                
-            }
-
-            if (str.trim().length() != 0)
-                throw new SAXException("Unexpected character data.");
-
-        }
-
-
-    }
-
-
-}
diff --git a/src/main/java/net/sourceforge/guacamole/net/authentication/basic/BasicGuacamoleClientProvider.java b/src/main/java/net/sourceforge/guacamole/net/authentication/basic/BasicGuacamoleClientProvider.java
deleted file mode 100644 (file)
index dfd9b38..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-
-package net.sourceforge.guacamole.net.authentication.basic;
-
-import javax.servlet.http.HttpSession;
-import net.sourceforge.guacamole.GuacamoleTCPClient;
-import net.sourceforge.guacamole.GuacamoleException;
-import net.sourceforge.guacamole.net.Configuration;
-import net.sourceforge.guacamole.net.GuacamoleProperties;
-import net.sourceforge.guacamole.net.authentication.GuacamoleClientProvider;
-
-/*
- *  Guacamole - Clientless Remote Desktop
- *  Copyright (C) 2010  Michael Jumper
- *
- *  This program is free software: you can redistribute it and/or modify
- *  it under the terms of the GNU Affero General Public License as published by
- *  the Free Software Foundation, either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU Affero General Public License for more details.
- *
- *  You should have received a copy of the GNU Affero General Public License
- *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-public class BasicGuacamoleClientProvider implements GuacamoleClientProvider {
-
-    public GuacamoleTCPClient createClient(HttpSession session) throws GuacamoleException {
-
-        // Retrieve authorized config data from session
-        Configuration config = (Configuration) session.getAttribute("BASIC-LOGIN-AUTH");
-
-        // If no data, not authorized
-        if (config == null)
-            throw new GuacamoleException("Unauthorized");
-
-        String hostname = GuacamoleProperties.getProperty("guacd-hostname");
-        int port = GuacamoleProperties.getIntProperty("guacd-port", null);
-
-        GuacamoleTCPClient client = new GuacamoleTCPClient(hostname, port);
-        client.connect(config);
-
-        // Return authorized session
-        return client;
-
-    }
-
-}
diff --git a/src/main/java/net/sourceforge/guacamole/net/authentication/basic/BasicLogin.java b/src/main/java/net/sourceforge/guacamole/net/authentication/basic/BasicLogin.java
deleted file mode 100644 (file)
index 8347202..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-
-package net.sourceforge.guacamole.net.authentication.basic;
-
-/*
- *  Guacamole - Clientless Remote Desktop
- *  Copyright (C) 2010  Michael Jumper
- *
- *  This program is free software: you can redistribute it and/or modify
- *  it under the terms of the GNU Affero General Public License as published by
- *  the Free Software Foundation, either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU Affero General Public License for more details.
- *
- *  You should have received a copy of the GNU Affero General Public License
- *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-import net.sourceforge.guacamole.GuacamoleException;
-import net.sourceforge.guacamole.net.Configuration;
-import net.sourceforge.guacamole.net.GuacamoleProperties;
-
-public class BasicLogin extends HttpServlet {
-
-    private AuthenticationProvider authProvider;
-
-    @Override
-    public void init() throws ServletException {
-
-        // Get auth provider instance
-        try {
-            String authProviderClassName = GuacamoleProperties.getProperty("auth-provider");
-            Object obj = Class.forName(authProviderClassName).getConstructor().newInstance();
-            if (!(obj instanceof AuthenticationProvider))
-                throw new ServletException("Specified authentication provider class is not a AuthenticationProvider.");
-
-            authProvider = (AuthenticationProvider) obj;
-        }
-        catch (GuacamoleException e) {
-            throw new ServletException(e);
-        }
-        catch (ClassNotFoundException e) {
-            throw new ServletException("Authentication provider class not found", e);
-        }
-        catch (NoSuchMethodException e) {
-            throw new ServletException("Default constructor for authentication provider not present", e);
-        }
-        catch (SecurityException e) {
-            throw new ServletException("Creation of authentication provider disallowed; check your security settings", e);
-        }
-        catch (InstantiationException e) {
-            throw new ServletException("Unable to instantiate authentication provider", e);
-        }
-        catch (IllegalAccessException e) {
-            throw new ServletException("Unable to access default constructor of authentication provider", e);
-        }
-        catch (InvocationTargetException e) {
-            throw new ServletException("Internal error in constructor of authentication provider", e.getTargetException());
-        }
-
-    }
-
-    public static interface AuthenticationProvider {
-        public Configuration getAuthorizedConfiguration(String username, String password) throws GuacamoleException;
-    }
-
-    @Override
-    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-
-        // Retrieve username and password from parms
-        String username = req.getParameter("username");
-        String password = req.getParameter("password");
-
-        // Validate username and password
-        try {
-
-            Configuration config = authProvider.getAuthorizedConfiguration(username, password);
-            if (config != null) {
-
-                // Store authorized configuration
-                HttpSession session = req.getSession(true);
-                session.setAttribute(
-                    "BASIC-LOGIN-AUTH",
-                    config
-                );
-
-                // Success
-                return;
-
-            }
-
-            // Report "forbidden" on any failure
-            resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Login invalid");
-        }
-        catch (GuacamoleException e) {
-            throw new ServletException("Error validating credentials", e);
-        }
-
-    }
-
-
-}
diff --git a/src/main/java/net/sourceforge/guacamole/net/basic/BasicFileAuthenticationProvider.java b/src/main/java/net/sourceforge/guacamole/net/basic/BasicFileAuthenticationProvider.java
new file mode 100644 (file)
index 0000000..f81503f
--- /dev/null
@@ -0,0 +1,359 @@
+
+package net.sourceforge.guacamole.net.basic;
+
+/*
+ *  Guacamole - Clientless Remote Desktop
+ *  Copyright (C) 2010  Michael Jumper
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Affero General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Affero General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Affero General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.io.File;
+import java.io.IOException;
+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.Configuration;
+import net.sourceforge.guacamole.net.GuacamoleProperties;
+import org.xml.sax.Attributes;
+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 BasicLogin.AuthenticationProvider {
+
+    private long mappingTime;
+    private Map<String, AuthInfo> mapping;
+
+    private File getUserMappingFile() throws GuacamoleException {
+
+        // Get user mapping filename
+        String filename = GuacamoleProperties.getProperty("basic-user-mapping");
+        if (filename == null)
+            return null;
+
+        return new File(filename);
+
+    }
+
+    public synchronized void init() throws GuacamoleException {
+
+        // Get user mapping file
+        File mapFile = getUserMappingFile();
+        if (mapFile == null)
+            throw new GuacamoleException("Missing \"basic-user-mapping\" parameter required for basic login.");
+
+        // Parse document
+        try {
+
+            BasicUserMappingContentHandler contentHandler = new BasicUserMappingContentHandler();
+
+            XMLReader parser = XMLReaderFactory.createXMLReader();
+            parser.setContentHandler(contentHandler);
+            parser.parse(mapFile.getAbsolutePath());
+
+            mappingTime = mapFile.lastModified();
+            mapping = contentHandler.getUserMapping();
+
+        }
+        catch (IOException e) {
+            throw new GuacamoleException("Error reading basic user mapping file.", e);
+        }
+        catch (SAXException e) {
+            throw new GuacamoleException("Error parsing basic user mapping XML.", e);
+        }
+
+    }
+
+    @Override
+    public Configuration getAuthorizedConfiguration(String username, String password) throws GuacamoleException {
+
+        // Check mapping file mod time
+        File userMappingFile = getUserMappingFile();
+        if (userMappingFile.exists() && mappingTime < userMappingFile.lastModified()) {
+
+            // If modified recently, gain exclusive access and recheck
+            synchronized (this) {
+                if (userMappingFile.exists() && mappingTime < userMappingFile.lastModified())
+                    init(); // If still not up to date, re-init
+            }
+
+        }
+
+        AuthInfo info = mapping.get(username);
+        if (info != null && info.validate(username, password))
+            return info.getConfiguration();
+
+        return null;
+
+    }
+
+    public static class AuthInfo {
+
+        public static enum Encoding {
+            PLAIN_TEXT,
+            MD5
+        }
+
+        private String auth_username;
+        private String auth_password;
+        private Encoding auth_encoding;
+
+        private Configuration config;
+
+        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 Configuration();
+        }
+
+        private static final char HEX_CHARS[] = {
+            '0', '1', '2', '3', '4', '5', '6', '7',
+            '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+        };
+
+        public static String getHexString(byte[] bytes) {
+
+            if (bytes == null)
+                return null;
+
+            StringBuilder hex = new StringBuilder(2 * bytes.length);
+            for (byte b : bytes) {
+                hex.append(HEX_CHARS[(b & 0xF0) >> 4])
+                   .append(HEX_CHARS[(b & 0x0F)     ]);
+            }
+
+            return hex.toString();
+
+        }
+
+
+        public boolean validate(String username, String password) {
+
+            // If username matches
+            if (username != null && password != null && username.equals(auth_username)) {
+
+                switch (auth_encoding) {
+
+                    case PLAIN_TEXT:
+
+                        // Compare plaintext
+                        return password.equals(auth_password);
+
+                    case MD5:
+
+                        // Compare hashed password
+                        try {
+                            MessageDigest digest = MessageDigest.getInstance("MD5");
+                            String hashedPassword = getHexString(digest.digest(password.getBytes()));
+                            return hashedPassword.equals(auth_password.toUpperCase());
+                        }
+                        catch (NoSuchAlgorithmException e) {
+                            throw new UnsupportedOperationException("Unexpected lack of MD5 support.", e);
+                        }
+
+                }
+
+            }
+
+            return false;
+
+        }
+
+        public Configuration getConfiguration() {
+            return config;
+        }
+
+    }
+
+
+    private static class BasicUserMappingContentHandler extends DefaultHandler {
+
+        private Map<String, AuthInfo> authMapping = new HashMap<String, AuthInfo>();
+
+        public Map<String, AuthInfo> getUserMapping() {
+            return Collections.unmodifiableMap(authMapping);
+        }
+
+        private enum State {
+            ROOT,
+            USER_MAPPING,
+            AUTH_INFO,
+            PROTOCOL,
+            PARAMETER,
+            END;
+        }
+
+        private State state = State.ROOT;
+        private AuthInfo current = null;
+        private String currentParameter = null;
+
+        @Override
+        public void endElement(String uri, String localName, String qName) throws SAXException {
+
+            switch (state)  {
+
+                case USER_MAPPING:
+
+                    if (localName.equals("user-mapping")) {
+                        state = State.END;
+                        return;
+                    }
+
+                    break;
+
+                case AUTH_INFO:
+
+                    if (localName.equals("authorize")) {
+
+                        // Finalize mapping for this user
+                        authMapping.put(
+                            current.auth_username,
+                            current
+                        );
+
+                        state = State.USER_MAPPING;
+                        return;
+                    }
+
+                    break;
+
+                case PROTOCOL:
+
+                    if (localName.equals("protocol")) {
+                        state = State.AUTH_INFO;
+                        return;
+                    }
+
+                    break;
+
+                case PARAMETER:
+
+                    if (localName.equals("param")) {
+                        state = State.AUTH_INFO;
+                        return;
+                    }
+
+                    break;
+
+            }
+
+            throw new SAXException("Tag not yet complete: " + localName);
+
+        }
+
+        @Override
+        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+
+            switch (state)  {
+
+                // Document must be <user-mapping>
+                case ROOT:
+
+                    if (localName.equals("user-mapping")) {
+                        state = State.USER_MAPPING;
+                        return;
+                    }
+
+                    break;
+
+                // Only <authorize> tags allowed in main document
+                case USER_MAPPING:
+
+                    if (localName.equals("authorize")) {
+
+                        AuthInfo.Encoding encoding;
+                        String encodingString = attributes.getValue("encoding");
+                        if (encodingString == null)
+                            encoding = AuthInfo.Encoding.PLAIN_TEXT;
+                        else if (encodingString.equals("plain"))
+                            encoding = AuthInfo.Encoding.PLAIN_TEXT;
+                        else if (encodingString.equals("md5"))
+                            encoding = AuthInfo.Encoding.MD5;
+                        else
+                            throw new SAXException("Invalid encoding type");
+
+
+                        current = new AuthInfo(
+                            attributes.getValue("username"),
+                            attributes.getValue("password"),
+                            encoding
+                        );
+
+                        // Next state
+                        state = State.AUTH_INFO;
+                        return;
+                    }
+
+                    break;
+
+                case AUTH_INFO:
+
+                    if (localName.equals("protocol")) {
+                        // Next state
+                        state = State.PROTOCOL;
+                        return;
+                    }
+
+                    if (localName.equals("param")) {
+
+                        currentParameter = attributes.getValue("name");
+                        if (currentParameter == null)
+                            throw new SAXException("Attribute \"name\" required for param tag.");
+
+                        // Next state
+                        state = State.PARAMETER;
+                        return;
+                    }
+
+                    break;
+
+            }
+
+            throw new SAXException("Unexpected tag: " + localName);
+
+        }
+
+        @Override
+        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);
+                    return;
+
+                case PARAMETER:
+                    current.getConfiguration().setParameter(currentParameter, str);
+                    return;
+                
+            }
+
+            if (str.trim().length() != 0)
+                throw new SAXException("Unexpected character data.");
+
+        }
+
+
+    }
+
+
+}
diff --git a/src/main/java/net/sourceforge/guacamole/net/basic/BasicGuacamoleTunnelServlet.java b/src/main/java/net/sourceforge/guacamole/net/basic/BasicGuacamoleTunnelServlet.java
new file mode 100644 (file)
index 0000000..08c5b15
--- /dev/null
@@ -0,0 +1,59 @@
+package net.sourceforge.guacamole.net.basic;
+
+/*
+ *  Guacamole - Clientless Remote Desktop
+ *  Copyright (C) 2010  Michael Jumper
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Affero General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Affero General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Affero General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import net.sourceforge.guacamole.GuacamoleException;
+import net.sourceforge.guacamole.GuacamoleTCPClient;
+import net.sourceforge.guacamole.net.Configuration;
+import net.sourceforge.guacamole.net.GuacamoleProperties;
+import net.sourceforge.guacamole.net.GuacamoleSession;
+import net.sourceforge.guacamole.net.tunnel.GuacamoleTunnelServlet;
+
+public class BasicGuacamoleTunnelServlet extends GuacamoleTunnelServlet {
+
+    @Override
+    protected void doConnect(HttpServletRequest request, HttpServletResponse response) throws GuacamoleException {
+
+        // Session must already exist from login
+        HttpSession httpSession = request.getSession(false);
+
+        // Retrieve authorized config data from session
+        Configuration config = (Configuration) httpSession.getAttribute("BASIC-LOGIN-AUTH");
+
+        // If no data, not authorized
+        if (config == null)
+            throw new GuacamoleException("Unauthorized");
+
+        String hostname = GuacamoleProperties.getProperty("guacd-hostname");
+        int port = GuacamoleProperties.getIntProperty("guacd-port", null);
+
+        GuacamoleTCPClient client = new GuacamoleTCPClient(hostname, port);
+        client.connect(config);
+
+        // Set client for session
+        GuacamoleSession session = new GuacamoleSession(httpSession);
+        session.attachClient(client);
+
+    }
+
+}
+
diff --git a/src/main/java/net/sourceforge/guacamole/net/basic/BasicLogin.java b/src/main/java/net/sourceforge/guacamole/net/basic/BasicLogin.java
new file mode 100644 (file)
index 0000000..8b12d49
--- /dev/null
@@ -0,0 +1,112 @@
+
+package net.sourceforge.guacamole.net.basic;
+
+/*
+ *  Guacamole - Clientless Remote Desktop
+ *  Copyright (C) 2010  Michael Jumper
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Affero General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Affero General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Affero General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import net.sourceforge.guacamole.GuacamoleException;
+import net.sourceforge.guacamole.net.Configuration;
+import net.sourceforge.guacamole.net.GuacamoleProperties;
+
+public class BasicLogin extends HttpServlet {
+
+    private AuthenticationProvider authProvider;
+
+    @Override
+    public void init() throws ServletException {
+
+        // Get auth provider instance
+        try {
+            String authProviderClassName = GuacamoleProperties.getProperty("auth-provider");
+            Object obj = Class.forName(authProviderClassName).getConstructor().newInstance();
+            if (!(obj instanceof AuthenticationProvider))
+                throw new ServletException("Specified authentication provider class is not a AuthenticationProvider.");
+
+            authProvider = (AuthenticationProvider) obj;
+        }
+        catch (GuacamoleException e) {
+            throw new ServletException(e);
+        }
+        catch (ClassNotFoundException e) {
+            throw new ServletException("Authentication provider class not found", e);
+        }
+        catch (NoSuchMethodException e) {
+            throw new ServletException("Default constructor for authentication provider not present", e);
+        }
+        catch (SecurityException e) {
+            throw new ServletException("Creation of authentication provider disallowed; check your security settings", e);
+        }
+        catch (InstantiationException e) {
+            throw new ServletException("Unable to instantiate authentication provider", e);
+        }
+        catch (IllegalAccessException e) {
+            throw new ServletException("Unable to access default constructor of authentication provider", e);
+        }
+        catch (InvocationTargetException e) {
+            throw new ServletException("Internal error in constructor of authentication provider", e.getTargetException());
+        }
+
+    }
+
+    public static interface AuthenticationProvider {
+        public Configuration getAuthorizedConfiguration(String username, String password) throws GuacamoleException;
+    }
+
+    @Override
+    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+
+        // Retrieve username and password from parms
+        String username = req.getParameter("username");
+        String password = req.getParameter("password");
+
+        // Validate username and password
+        try {
+
+            Configuration config = authProvider.getAuthorizedConfiguration(username, password);
+            if (config != null) {
+
+                // Store authorized configuration
+                HttpSession session = req.getSession(true);
+                session.setAttribute(
+                    "BASIC-LOGIN-AUTH",
+                    config
+                );
+
+                // Success
+                return;
+
+            }
+
+            // Report "forbidden" on any failure
+            resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Login invalid");
+        }
+        catch (GuacamoleException e) {
+            throw new ServletException("Error validating credentials", e);
+        }
+
+    }
+
+
+}
index c7cbc9f..bf0e6c6 100644 (file)
                        30
         </session-timeout>
     </session-config>
-    <!-- Guacamole Tunnel Servlets -->
-    <servlet>
-        <description>Connect servlet.</description>
-        <servlet-name>Connect</servlet-name>
-        <servlet-class>net.sourceforge.guacamole.net.tunnel.Connect</servlet-class>
-    </servlet>
-    <servlet-mapping>
-        <servlet-name>Connect</servlet-name>
-        <url-pattern>/connect</url-pattern>
-    </servlet-mapping>
-    <servlet>
-        <description>Outbound servlet.</description>
-        <servlet-name>Outbound</servlet-name>
-        <servlet-class>net.sourceforge.guacamole.net.tunnel.Outbound</servlet-class>
-    </servlet>
-    <servlet-mapping>
-        <servlet-name>Outbound</servlet-name>
-        <url-pattern>/outbound</url-pattern>
-    </servlet-mapping>
+
+    <!-- Guacamole Tunnel Servlet -->
     <servlet>
-        <description>Input servlet.</description>
-        <servlet-name>Inbound</servlet-name>
-        <servlet-class>net.sourceforge.guacamole.net.tunnel.Inbound</servlet-class>
+        <description>Tunnel servlet.</description>
+        <servlet-name>Tunnel</servlet-name>
+        <servlet-class>net.sourceforge.guacamole.net.basic.BasicGuacamoleTunnelServlet</servlet-class>
     </servlet>
     <servlet-mapping>
-        <servlet-name>Inbound</servlet-name>
-        <url-pattern>/inbound</url-pattern>
+        <servlet-name>Tunnel</servlet-name>
+        <url-pattern>/tunnel</url-pattern>
     </servlet-mapping>
-    <!-- Basic Login Servlets -->
+
+    <!-- Basic Login Servlet -->
     <servlet>
         <servlet-name>BasicLogin</servlet-name>
-        <servlet-class>net.sourceforge.guacamole.net.authentication.basic.BasicLogin</servlet-class>
+        <servlet-class>net.sourceforge.guacamole.net.basic.BasicLogin</servlet-class>
     </servlet>
     <servlet-mapping>
         <servlet-name>BasicLogin</servlet-name>
         <url-pattern>/login</url-pattern>
     </servlet-mapping>
+
 </web-app>
index 926fb9c..47d0e9b 100644 (file)
                 window.onresize();
 
                 // Instantiate client
-                var guac = new GuacamoleClient(display);
+                var guac = new GuacamoleClient(display, "tunnel");
 
                 var state = document.getElementById("state");
                 guac.setOnStateChangeHandler(function(clientState) {