* 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.basic.properties.BasicGuacamoleProperties;
-import net.sourceforge.guacamole.protocol.GuacamoleConfiguration;
+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;
+/**
+ * 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);
}
// 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();
}
@Override
- public GuacamoleConfiguration getAuthorizedConfiguration(String username, String password) throws GuacamoleException {
+ public Map<String, GuacamoleConfiguration> getAuthorizedConfigurations(Credentials credentials) throws GuacamoleException {
// Check mapping file mod time
File userMappingFile = getUserMappingFile();
throw new GuacamoleException("User mapping could not be read.");
// Validate and return info for given user and pass
- AuthInfo info = mapping.get(username);
- if (info != null && info.validate(username, password))
- return info.getConfiguration();
+ AuthInfo info = mapping.get(credentials.getUsername());
+ if (info != null && info.validate(credentials.getUsername(), credentials.getPassword()))
+ return info.getConfigurations();
+ // Unauthorized
return null;
}
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[] = {
}
- 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 {
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 {
}
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;
}
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;
}
break;
- // Only <authorize> tags allowed in main document
case USER_MAPPING:
if (localName.equals("authorize")) {
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;
return;
}
- break;
+ break;
}
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().setParameter(currentParameter, str);
+ case DEFAULT_CONNECTION_PARAMETER:
+
+ current.getConfiguration(currentConnection)
+ .setParameter(currentParameter, str);
return;
}