diff --git a/extensions/guacamole-auth-mysql/pom.xml b/extensions/guacamole-auth-mysql/pom.xml
index 952db2420..c193336ad 100644
--- a/extensions/guacamole-auth-mysql/pom.xml
+++ b/extensions/guacamole-auth-mysql/pom.xml
@@ -93,7 +93,18 @@
mybatis-guice
3.2
-
+
+
+ com.google.collections
+ google-collections
+ 1.0
+
+
+ net.sourceforge.guacamole
+ guacamole-auth-mysql
+ 0.8.0
+ jar
+
diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProvider.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProvider.java
index 6505dbea8..db95428a7 100644
--- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProvider.java
+++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProvider.java
@@ -52,6 +52,10 @@ import net.sourceforge.guacamole.net.auth.mysql.dao.guacamole.SystemPermissionMa
import net.sourceforge.guacamole.net.auth.mysql.dao.guacamole.UserMapper;
import net.sourceforge.guacamole.net.auth.mysql.dao.guacamole.UserPermissionMapper;
import net.sourceforge.guacamole.net.auth.mysql.properties.MySQLGuacamoleProperties;
+import net.sourceforge.guacamole.net.auth.mysql.utility.PasswordEncryptionUtility;
+import net.sourceforge.guacamole.net.auth.mysql.utility.SaltUtility;
+import net.sourceforge.guacamole.net.auth.mysql.utility.SecureRandomSaltUtility;
+import net.sourceforge.guacamole.net.auth.mysql.utility.Sha256PasswordEncryptionUtility;
import net.sourceforge.guacamole.properties.GuacamoleProperties;
import org.mybatis.guice.MyBatisModule;
import org.mybatis.guice.datasource.builtin.PooledDataSourceProvider;
@@ -105,6 +109,9 @@ public class MySQLAuthenticationProvider implements AuthenticationProvider {
addMapperClass(UserMapper.class);
addMapperClass(UserPermissionMapper.class);
bind(MySQLUserContext.class);
+ bind(MySQLUser.class);
+ bind(SaltUtility.class).to(SecureRandomSaltUtility.class);
+ bind(PasswordEncryptionUtility.class).to(Sha256PasswordEncryptionUtility.class);
}
}
);
diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLUser.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLUser.java
index c0a351576..f29ec948a 100644
--- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLUser.java
+++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLUser.java
@@ -35,65 +35,116 @@
* ***** END LICENSE BLOCK ***** */
package net.sourceforge.guacamole.net.auth.mysql;
+import com.google.inject.Inject;
+import java.io.UnsupportedEncodingException;
+import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import net.sourceforge.guacamole.GuacamoleException;
import net.sourceforge.guacamole.net.auth.Credentials;
import net.sourceforge.guacamole.net.auth.User;
+import net.sourceforge.guacamole.net.auth.mysql.dao.guacamole.UserMapper;
+import net.sourceforge.guacamole.net.auth.mysql.model.guacamole.UserExample;
+import net.sourceforge.guacamole.net.auth.mysql.utility.PasswordEncryptionUtility;
+import net.sourceforge.guacamole.net.auth.mysql.utility.SaltUtility;
import net.sourceforge.guacamole.net.auth.permission.Permission;
/**
- *
+ * A MySQL based implementation of the User object.
* @author James Muehlner
*/
public class MySQLUser implements User {
- private String username;
- private String userID;
- private String salt;
+ private net.sourceforge.guacamole.net.auth.mysql.model.guacamole.User user;
- MySQLUser(Credentials credentials) {
- //TODO: load the user from the DB if the credentials are correct,
- // otherwise, throw some kind of exception
+ @Inject
+ UserMapper userDao;
+
+ @Inject
+ PasswordEncryptionUtility passwordUtility;
+
+ @Inject
+ SaltUtility saltUtility;
+
+ Set permissions;
+
+ MySQLUser() {
+ user = new net.sourceforge.guacamole.net.auth.mysql.model.guacamole.User();
+ permissions = new HashSet();
+ }
+
+ /**
+ * Create the user, throwing an exception if the credentials do not match what's in the database.
+ * @param credentials
+ * @throws GuacamoleException
+ */
+ void init (Credentials credentials) throws GuacamoleException {
+ UserExample userExample = new UserExample();
+ userExample.createCriteria().andUsernameEqualTo(credentials.getUsername());
+ List users = userDao.selectByExample(userExample);
+ if(users.size() > 1) // the unique constraint on the table should prevent this
+ throw new GuacamoleException("Multiple users found with the same username: " + credentials.getUsername());
+ if(users.isEmpty())
+ throw new GuacamoleException("No user found with the supplied credentials");
+ user = users.get(0);
+ // check password
+ if(!passwordUtility.checkCredentials(credentials, user.getPassword_hash(), user.getUsername(), user.getPassword_salt()))
+ throw new GuacamoleException("No user found with the supplied credentials");
+ }
+
+ void init (User user) {
+ this.setPassword(user.getPassword());
+ this.setUsername(user.getUsername());
+ }
+
+ public net.sourceforge.guacamole.net.auth.mysql.model.guacamole.User getUser() {
+ return user;
}
@Override
public String getUsername() {
- return username;
+ return user.getUsername();
}
@Override
public void setUsername(String username) {
- throw new UnsupportedOperationException("Not supported yet.");
+ user.setUsername(username);
}
@Override
public String getPassword() {
- throw new UnsupportedOperationException("Not supported yet.");
+ try {
+ return new String(user.getPassword_hash(), "UTF-8");
+ } catch (UnsupportedEncodingException ex) {
+ throw new RuntimeException(ex); // should not happen
+ }
}
@Override
public void setPassword(String password) {
- throw new UnsupportedOperationException("Not supported yet.");
+ String salt = saltUtility.generateSalt();
+ user.setPassword_salt(salt);
+ byte[] hash = passwordUtility.createPasswordHash(password, salt);
+ user.setPassword_hash(hash);
}
@Override
public Set getPermissions() throws GuacamoleException {
- throw new UnsupportedOperationException("Not supported yet.");
+ return permissions;
}
@Override
public boolean hasPermission(Permission permission) throws GuacamoleException {
- throw new UnsupportedOperationException("Not supported yet.");
+ return permissions.contains(permission);
}
@Override
public void addPermission(Permission permission) throws GuacamoleException {
- throw new UnsupportedOperationException("Not supported yet.");
+ permissions.add(permission);
}
@Override
public void removePermission(Permission permission) throws GuacamoleException {
- throw new UnsupportedOperationException("Not supported yet.");
+ permissions.remove(permission);
}
-
}
diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLUserContext.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLUserContext.java
index 6de80c19e..026533aea 100644
--- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLUserContext.java
+++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLUserContext.java
@@ -53,13 +53,16 @@ public class MySQLUserContext implements UserContext {
private Logger logger = LoggerFactory.getLogger(MySQLUserContext.class);
- void init(Credentials credentials) {
- // load the required data with the provided credentials
+ @Inject
+ private MySQLUser user;
+
+ void init(Credentials credentials) throws GuacamoleException {
+ user.init(credentials);
}
@Override
public User self() {
- throw new UnsupportedOperationException("Not supported yet.");
+ return user;
}
@Override
diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/UserDirectory.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/UserDirectory.java
new file mode 100644
index 000000000..09419b969
--- /dev/null
+++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/UserDirectory.java
@@ -0,0 +1,72 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package net.sourceforge.guacamole.net.auth.mysql;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import net.sourceforge.guacamole.GuacamoleException;
+import net.sourceforge.guacamole.net.auth.Directory;
+import net.sourceforge.guacamole.net.auth.User;
+import net.sourceforge.guacamole.net.auth.mysql.dao.guacamole.UserMapper;
+
+/**
+ * A MySQL based implementation of the User Directory.
+ * @author James Muehlner
+ */
+public class UserDirectory implements Directory {
+
+ private Map userMap = new HashMap();
+
+ @Inject
+ UserMapper userDAO;
+
+ @Inject
+ Provider mySQLUserProvider;
+
+ private MySQLUser getMySQLUser(User user) {
+ MySQLUser mySQLUser = mySQLUserProvider.get();
+ mySQLUser.init(user);
+ return mySQLUser;
+ }
+
+ @Override
+ public User get(String identifier) throws GuacamoleException {
+ return userMap.get(identifier);
+ }
+
+ @Override
+ public Set getIdentifiers() throws GuacamoleException {
+ return userMap.keySet();
+ }
+
+ @Override
+ public void add(User object) throws GuacamoleException {
+ MySQLUser mySQLUser = getMySQLUser(object);
+ userDAO.insert(mySQLUser.getUser());
+ userMap.put(mySQLUser.getUsername(), mySQLUser);
+ }
+
+ @Override
+ public void update(User object) throws GuacamoleException {
+ if(!userMap.containsKey(object.getUsername()))
+ throw new GuacamoleException("User not found in Directory.");
+ MySQLUser mySQLUser = getMySQLUser(object);
+ userDAO.updateByPrimaryKey(mySQLUser.getUser());
+ userMap.put(object.getUsername(), mySQLUser);
+ }
+
+ @Override
+ public void remove(String identifier) throws GuacamoleException {
+ User user = userMap.get(identifier);
+ if(user == null)
+ throw new GuacamoleException("User not found in Directory.");
+ MySQLUser mySQLUser = getMySQLUser(user);
+ userDAO.deleteByPrimaryKey(mySQLUser.getUser().getUser_id());
+ userMap.remove(user.getUsername());
+ }
+}
diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/utility/PasswordEncryptionUtility.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/utility/PasswordEncryptionUtility.java
new file mode 100644
index 000000000..f9d7a4963
--- /dev/null
+++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/utility/PasswordEncryptionUtility.java
@@ -0,0 +1,64 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is guacamole-auth-mysql.
+ *
+ * The Initial Developer of the Original Code is
+ * James Muehlner.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+package net.sourceforge.guacamole.net.auth.mysql.utility;
+
+import net.sourceforge.guacamole.net.auth.Credentials;
+
+/**
+ *
+ * @author James Muehlner
+ */
+public interface PasswordEncryptionUtility {
+
+ /**
+ * Checks if the provided Credentials are correct, compared with what the values from the database.
+ * @param credentials
+ * @param dbPasswordHash
+ * @param dbUsername
+ * @param dbSalt
+ * @return true if the provided credentials match what's in the database for that user.
+ */
+ public boolean checkCredentials(Credentials credentials, byte[] dbPasswordHash, String dbUsername, String dbSalt);
+
+ /**
+ * Creates a password hash based on the provided username, password, and salt.
+ * @param username
+ * @param password
+ * @param salt
+ * @return the generated password hash.
+ */
+ public byte[] createPasswordHash(String password, String salt);
+}
diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/utility/SaltUtility.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/utility/SaltUtility.java
new file mode 100644
index 000000000..fd4661769
--- /dev/null
+++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/utility/SaltUtility.java
@@ -0,0 +1,50 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is guacamole-auth-mysql.
+ *
+ * The Initial Developer of the Original Code is
+ * James Muehlner.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+package net.sourceforge.guacamole.net.auth.mysql.utility;
+
+import net.sourceforge.guacamole.GuacamoleException;
+
+/**
+ *
+ * @author James Muehlner
+ */
+public interface SaltUtility {
+ /**
+ * Generates a new String that can be used as a password salt.
+ * @return a new salt for password encryption.
+ */
+ public String generateSalt();
+}
diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/utility/SecureRandomSaltUtility.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/utility/SecureRandomSaltUtility.java
new file mode 100644
index 000000000..63d37d3a5
--- /dev/null
+++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/utility/SecureRandomSaltUtility.java
@@ -0,0 +1,28 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package net.sourceforge.guacamole.net.auth.mysql.utility;
+
+import java.io.UnsupportedEncodingException;
+import java.security.SecureRandom;
+
+/**
+ * Generates password salts via the SecureRandom utility.
+ * @author dagger10k
+ */
+public class SecureRandomSaltUtility implements SaltUtility {
+
+ SecureRandom secureRandom = new SecureRandom();
+
+ @Override
+ public String generateSalt() {
+ byte[] salt = new byte[32];
+ secureRandom.nextBytes(salt);
+ try {
+ return new String(salt, "UTF-8");
+ } catch (UnsupportedEncodingException ex) { // should not happen
+ throw new RuntimeException(ex);
+ }
+ }
+}
diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/utility/Sha256PasswordEncryptionUtility.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/utility/Sha256PasswordEncryptionUtility.java
new file mode 100644
index 000000000..ff233d7f6
--- /dev/null
+++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/utility/Sha256PasswordEncryptionUtility.java
@@ -0,0 +1,79 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is guacamole-auth-mysql.
+ *
+ * The Initial Developer of the Original Code is
+ * James Muehlner.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+package net.sourceforge.guacamole.net.auth.mysql.utility;
+
+import com.google.common.base.Preconditions;
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import net.sourceforge.guacamole.net.auth.Credentials;
+
+/**
+ * Provides a SHA-256 based implementation of the password encryption functionality.
+ * @author James Muehlner
+ */
+public class Sha256PasswordEncryptionUtility implements PasswordEncryptionUtility {
+
+ @Override
+ public boolean checkCredentials(Credentials credentials, byte[] dbPasswordHash, String dbUsername, String dbSalt) {
+ Preconditions.checkNotNull(credentials);
+ Preconditions.checkNotNull(dbPasswordHash);
+ Preconditions.checkNotNull(dbUsername);
+ Preconditions.checkNotNull(dbSalt);
+ byte[] passwordBytes = createPasswordHash(credentials.getPassword(), dbSalt);
+ return Arrays.equals(passwordBytes, dbPasswordHash);
+ }
+
+ @Override
+ public byte[] createPasswordHash(String password, String salt) {
+ Preconditions.checkNotNull(password);
+ Preconditions.checkNotNull(salt);
+ try {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+
+ StringBuilder builder = new StringBuilder();
+ builder.append(password);
+ builder.append(salt);
+ md.update(builder.toString().getBytes("UTF-8"));
+ return md.digest();
+ } catch (UnsupportedEncodingException ex) { // should not happen
+ throw new RuntimeException(ex);
+ } catch (NoSuchAlgorithmException ex) { // should not happen
+ throw new RuntimeException(ex);
+ }
+ }
+}