Working login page and user auth configuration

This commit is contained in:
Michael Jumper
2010-12-04 20:47:34 -08:00
parent a8d9d4f256
commit ff9e1664a6
10 changed files with 362 additions and 39 deletions

View File

@@ -1,6 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
<role rolename="guacamole"/>
<user username="guacamole" password="changeme" roles="guacamole"/>
</tomcat-users>

View File

@@ -18,12 +18,16 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/guacamole" docBase="/var/lib/guacamole/guacamole.war">
<!-- Change the lines below to match your Guacamole proxy -->
<Parameter name="hostname" value="localhost"/>
<Parameter name="port" value="1234"/>
<Parameter name="guacd-hostname" value="localhost"/>
<Parameter name="guacd-port" value="4822"/>
<!-- Session provider class (provides and configured guacamole session based on authentication information) -->
<Parameter name="session-provider" value="net.sourceforge.guacamole.basic.BasicGuacamoleSessionProvider"/>
<Parameter name="basic-user-mapping" value="/path/to/user-mapping.xml"/>
<Realm className="org.apache.catalina.realm.MemoryRealm" pathname="conf/guacamole-users.xml"/>
</Context>

View File

@@ -0,0 +1,11 @@
<user-mapping>
<!-- Per-user authentication and config information -->
<authorize username="USERNAME" password="PASSWORD">
<protocol>vnc</protocol>
<hostname>localhost</hostname>
<port>5900</port>
<password>VNCPASS</password>
</authorize>
</user-mapping>

View File

@@ -1,8 +1,10 @@
package net.sourceforge.guacamole.net;
package net.sourceforge.guacamole.basic;
import javax.servlet.http.HttpSession;
import net.sourceforge.guacamole.GuacamoleException;
import net.sourceforge.guacamole.net.GuacamoleSession;
import net.sourceforge.guacamole.net.GuacamoleSessionProvider;
/*
* Guacamole - Clientless Remote Desktop
@@ -26,11 +28,21 @@ public class BasicGuacamoleSessionProvider implements GuacamoleSessionProvider {
public GuacamoleSession createSession(HttpSession session) throws GuacamoleException {
// Retrieve authorized config data from session
BasicLogin.AuthorizedConfiguration config = (BasicLogin.AuthorizedConfiguration)
session.getAttribute("BASIC-LOGIN-AUTH");
// If no data, not authorized
if (config == null)
throw new GuacamoleException("Unauthorized");
// Configure session from authorized config info
GuacamoleSession guacSession = new GuacamoleSession(session);
guacSession.setConnection(config.getProtocol(), config.getHostname(), config.getPort());
if (config.getPassword() != null)
guacSession.setPassword(config.getPassword());
guacSession.setConnection("vnc", "localhost", 5901);
guacSession.setPassword("potato");
// Return authorized session
return guacSession;
}

View File

@@ -0,0 +1,125 @@
package net.sourceforge.guacamole.basic;
import java.io.IOException;
import java.util.Map;
import javax.servlet.ServletContext;
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.basic.BasicUserMappingContentHandler.AuthInfo;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
public class BasicLogin extends HttpServlet {
private Map<String, AuthInfo> mapping;
// Added to session when session validated
public class AuthorizedConfiguration {
private String protocol;
private String hostname;
private int port;
private String password;
public AuthorizedConfiguration(String protocol, String hostname, int port, String password) {
this.protocol = protocol;
this.hostname = hostname;
this.port = port;
this.password = password;
}
public String getHostname() {
return hostname;
}
public String getPassword() {
return password;
}
public int getPort() {
return port;
}
public String getProtocol() {
return protocol;
}
}
@Override
public void init() throws ServletException {
// Get user mapping filename
ServletContext context = getServletContext();
String filename = context.getInitParameter("basic-user-mapping");
if (filename == null)
throw new ServletException("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(filename);
mapping = contentHandler.getUserMapping();
}
catch (IOException e) {
throw new ServletException("Error reading basic user mapping file.", e);
}
catch (SAXException e) {
throw new ServletException("Error parsing basic user mapping XML.", e);
}
}
@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");
// Retrieve corresponding authorization info
AuthInfo info = mapping.get(username);
if (info != null) {
// Validate username and password
if (info.getAuthorizedUsername().equals(username)
&& info.getAuthorizedPassword().equals(password)) {
// Store authorized configuration
HttpSession session = req.getSession(true);
session.setAttribute(
"BASIC-LOGIN-AUTH",
new AuthorizedConfiguration(
info.getProtocol(),
info.getHostname(),
info.getPort(),
info.getPassword()
)
);
// Success
return;
}
}
// Report "forbidden" on any failure
resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Login invalid");
}
}

View File

@@ -0,0 +1,150 @@
package net.sourceforge.guacamole.basic;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class BasicUserMappingContentHandler extends DefaultHandler {
private Map<String, AuthInfo> authMapping = new HashMap<String, AuthInfo>();
public Map<String, AuthInfo> getUserMapping() {
return Collections.unmodifiableMap(authMapping);
}
public class AuthInfo {
private String auth_username;
private String auth_password;
private String protocol;
private String hostname;
private int port;
private String password;
public AuthInfo(String auth_username, String auth_password) {
this.auth_username = auth_username;
this.auth_password = auth_password;
}
public String getAuthorizedUsername() {
return auth_username;
}
public String getAuthorizedPassword() {
return auth_password;
}
public String getHostname() {
return hostname;
}
public String getPassword() {
return password;
}
public int getPort() {
return port;
}
public String getProtocol() {
return protocol;
}
}
private AuthInfo current;
private enum AUTH_INFO_STATE {
PROTOCOL,
HOSTNAME,
PORT,
PASSWORD
};
private AUTH_INFO_STATE infoState;
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (localName.equals("authorize")) {
// Finalize mapping for this user
authMapping.put(
current.getAuthorizedUsername(),
current
);
}
infoState = null;
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (localName.equals("authorize")) {
current = new AuthInfo(
attributes.getValue("username"),
attributes.getValue("password")
);
infoState = null;
}
else if (localName.equals("protocol"))
infoState = AUTH_INFO_STATE.PROTOCOL;
else if (localName.equals("hostname"))
infoState = AUTH_INFO_STATE.HOSTNAME;
else if (localName.equals("port"))
infoState = AUTH_INFO_STATE.PORT;
else if (localName.equals("password"))
infoState = AUTH_INFO_STATE.PASSWORD;
else
infoState = null;
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
String str = new String(ch, start, length);
if (infoState == null)
return;
switch (infoState) {
case PROTOCOL:
current.protocol = str;
break;
case HOSTNAME:
current.hostname = str;
break;
case PORT:
current.port = Integer.parseInt(str);
break;
case PASSWORD:
current.password = str;
break;
}
}
}

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="">
<!-- Hostname and port of guacamole proxy -->
<Parameter name="guacd-hostname" value="localhost"/>
<Parameter name="guacd-port" value="4822"/>
<!-- Session provider class (provides and configured guacamole session based on authentication information) -->
<Parameter name="session-provider" value="net.sourceforge.guacamole.net.BasicGuacamoleSessionProvider"/>
</Context>

View File

@@ -17,9 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- Basic config -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
@@ -29,9 +28,8 @@
30
</session-timeout>
</session-config>
<!-- Servlets -->
<!-- Guacamole Tunnel Servlets -->
<servlet>
<description>Connect servlet.</description>
<servlet-name>Connect</servlet-name>
@@ -41,7 +39,6 @@
<servlet-name>Connect</servlet-name>
<url-pattern>/connect</url-pattern>
</servlet-mapping>
<servlet>
<description>Outbound servlet.</description>
<servlet-name>Outbound</servlet-name>
@@ -51,7 +48,6 @@
<servlet-name>Outbound</servlet-name>
<url-pattern>/outbound</url-pattern>
</servlet-mapping>
<servlet>
<description>Input servlet.</description>
<servlet-name>Inbound</servlet-name>
@@ -61,5 +57,15 @@
<servlet-name>Inbound</servlet-name>
<url-pattern>/inbound</url-pattern>
</servlet-mapping>
<!-- Basic Login Servlets -->
<servlet>
<servlet-name>BasicLogin</servlet-name>
<servlet-class>net.sourceforge.guacamole.basic.BasicLogin</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>BasicLogin</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
</web-app>

View File

@@ -34,6 +34,13 @@ div#login-ui {
display: table;
}
p#login-error {
text-align: center;
background: #FDD;
color: red;
margin: 0.2em;
}
div#login-logo {
position: relative;
bottom: 0;
@@ -71,19 +78,18 @@ div#login-dialog h1 {
margin-top: 0;
margin-bottom: 0em;
text-align: center;
border-bottom: 1px solid silver;
padding-bottom: 0.5em;
}
div#login-dialog #buttons {
border-top: 1px solid silver;
padding-top: 0.5em;
text-align: center;
}
div#login-dialog #login-fields {
margin-top: 0.5em;
margin-bottom: 0.5em;
border-top: 1px solid silver;
border-bottom: 1px solid silver;
padding-top: 0.5em;
padding-bottom: 0.5em;
}
div.errorDialogOuter {

View File

@@ -40,6 +40,9 @@
<div id="login-dialog">
<h1>Guacamole Login</h1>
<p id="login-error"></p>
<form id="login-form" action="#">
<table id="login-fields">
<tr>
@@ -124,10 +127,33 @@
loginForm.onsubmit = function() {
// FIXME: Do ACTUAL login here
var username = document.getElementById("username");
var password = document.getElementById("password");
loginUI.style.display = "none";
startGuacamole();
var data =
"username=" + encodeURIComponent(username.value)
+ "&password=" + encodeURIComponent(password.value)
var xmlhttprequest = new XMLHttpRequest();
xmlhttprequest.open("POST", "login", false);
xmlhttprequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttprequest.setRequestHeader("Content-length", data.length);
xmlhttprequest.send(data);
if (xmlhttprequest.status == 200) {
loginUI.style.display = "none";
startGuacamole();
}
else {
var loginError = document.getElementById("login-error");
// Display error, reset and refocus password field
loginError.textContent = "Invalid login. Please try again.";
password.value = "";
password.focus();
}
return false;