mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 13:17:41 +00:00
Moved basic auth into default webapp
This commit is contained in:
@@ -1,318 +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.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 BasicLogin.AuthorizedConfiguration 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 new BasicLogin.AuthorizedConfiguration(
|
||||
info.getProtocol(),
|
||||
info.getHostname(),
|
||||
info.getPort(),
|
||||
info.getPassword()
|
||||
);
|
||||
|
||||
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 String protocol;
|
||||
private String hostname;
|
||||
private int port;
|
||||
private String password;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 String getHostname() {
|
||||
return hostname;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public String getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
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 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.auth_username,
|
||||
current
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
infoState = null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
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,50 +0,0 @@
|
||||
|
||||
package net.sourceforge.guacamole.net.authentication.basic;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
import net.sourceforge.guacamole.GuacamoleException;
|
||||
import net.sourceforge.guacamole.net.GuacamoleSession;
|
||||
import net.sourceforge.guacamole.net.authentication.GuacamoleSessionProvider;
|
||||
|
||||
/*
|
||||
* 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 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());
|
||||
|
||||
// Return authorized session
|
||||
return guacSession;
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -1,161 +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;
|
||||
|
||||
public class BasicLogin extends HttpServlet {
|
||||
|
||||
private Config config;
|
||||
|
||||
@Override
|
||||
public void init() throws ServletException {
|
||||
try {
|
||||
config = new Config();
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
throw new ServletException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class Config extends Configuration {
|
||||
|
||||
private AuthenticationProvider authProvider;
|
||||
|
||||
public Config() throws GuacamoleException {
|
||||
|
||||
// Get auth provider instance
|
||||
try {
|
||||
String authProviderClassName = readParameter("auth-provider");
|
||||
Object obj = Class.forName(authProviderClassName).getConstructor().newInstance();
|
||||
if (!(obj instanceof AuthenticationProvider))
|
||||
throw new GuacamoleException("Specified session provider class is not a GuacamoleSessionProvider");
|
||||
|
||||
authProvider = (AuthenticationProvider) obj;
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
throw new GuacamoleException("Session provider class not found", e);
|
||||
}
|
||||
catch (NoSuchMethodException e) {
|
||||
throw new GuacamoleException("Default constructor for session provider not present", e);
|
||||
}
|
||||
catch (SecurityException e) {
|
||||
throw new GuacamoleException("Creation of session provider disallowed; check your security settings", e);
|
||||
}
|
||||
catch (InstantiationException e) {
|
||||
throw new GuacamoleException("Unable to instantiate session provider", e);
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
throw new GuacamoleException("Unable to access default constructor of session provider", e);
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
throw new GuacamoleException("Internal error in constructor of session provider", e.getTargetException());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public AuthenticationProvider getAuthenticationProvider() {
|
||||
return authProvider;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static interface AuthenticationProvider {
|
||||
public AuthorizedConfiguration getAuthorizedConfiguration(String username, String password) throws GuacamoleException;
|
||||
}
|
||||
|
||||
// Added to session when session validated
|
||||
public static 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
|
||||
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 {
|
||||
|
||||
AuthorizedConfiguration info = config.getAuthenticationProvider().getAuthorizedConfiguration(username, password);
|
||||
if (info != null) {
|
||||
|
||||
// Store authorized configuration
|
||||
HttpSession session = req.getSession(true);
|
||||
session.setAttribute(
|
||||
"BASIC-LOGIN-AUTH",
|
||||
info
|
||||
);
|
||||
|
||||
// Success
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
// Report "forbidden" on any failure
|
||||
resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Login invalid");
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
throw new ServletException("Error validating credentials", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
Reference in New Issue
Block a user