mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 13:17:41 +00:00
Working login page and user auth configuration
This commit is contained in:
@@ -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>
|
@@ -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>
|
||||
|
||||
|
11
guacamole/web-client/doc/example/user-mapping.xml
Normal file
11
guacamole/web-client/doc/example/user-mapping.xml
Normal 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>
|
@@ -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;
|
||||
|
||||
}
|
@@ -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");
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -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>
|
@@ -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>
|
||||
|
@@ -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 {
|
||||
|
@@ -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;
|
||||
|
||||
|
Reference in New Issue
Block a user