diff --git a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.form b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.form
index e84af7c5e3..f6423007f7 100644
--- a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.form
+++ b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.form
@@ -40,6 +40,7 @@
+
@@ -53,6 +54,7 @@
+
@@ -85,6 +87,11 @@
+
+
+
+
+
@@ -152,6 +159,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java
index ba8cbb2bf6..0945bf67d3 100644
--- a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java
+++ b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java
@@ -90,15 +90,17 @@ public class ConnectDialog extends MageDialog {
public ConnectDialog() {
initComponents();
- this.txtUserName.addActionListener(connectAction);
this.txtServer.addActionListener(connectAction);
this.txtPort.addActionListener(connectAction);
+ this.txtUserName.addActionListener(connectAction);
+ this.txtPassword.addActionListener(connectAction);
}
public void showDialog() {
this.txtServer.setText(MageFrame.getPreferences().get("serverAddress", Config.serverName));
this.txtPort.setText(MageFrame.getPreferences().get("serverPort", Integer.toString(Config.port)));
this.txtUserName.setText(MageFrame.getPreferences().get("userName", ""));
+ this.txtPassword.setText(MageFrame.getPreferences().get("password", ""));
this.chkAutoConnect.setSelected(Boolean.parseBoolean(MageFrame.getPreferences().get(KEY_CONNECT_AUTO_CONNECT, "false")));
this.chkForceUpdateDB.setSelected(false); // has always to be set manually to force comparison
@@ -120,6 +122,7 @@ public class ConnectDialog extends MageDialog {
MageFrame.getPreferences().put("serverAddress", txtServer.getText().trim());
MageFrame.getPreferences().put("serverPort", txtPort.getText().trim());
MageFrame.getPreferences().put("userName", txtUserName.getText().trim());
+ MageFrame.getPreferences().put("password", txtPassword.getText().trim());
MageFrame.getPreferences().put(KEY_CONNECT_AUTO_CONNECT, Boolean.toString(chkAutoConnect.isSelected()));
}
@@ -139,6 +142,8 @@ public class ConnectDialog extends MageDialog {
txtPort = new javax.swing.JTextField();
lblUserName = new javax.swing.JLabel();
txtUserName = new javax.swing.JTextField();
+ lblPassword = new javax.swing.JLabel();
+ txtPassword = new javax.swing.JPasswordField();
lblFlag = new javax.swing.JLabel();
cbFlag = new mage.client.util.gui.countryBox.CountryComboBox();
chkAutoConnect = new javax.swing.JCheckBox();
@@ -175,6 +180,21 @@ public class ConnectDialog extends MageDialog {
lblUserName.setLabelFor(txtUserName);
lblUserName.setText("User name:");
+ txtUserName.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ txtUserNameActionPerformed(evt);
+ }
+ });
+
+ lblPassword.setLabelFor(txtPassword);
+ lblPassword.setText("Password:");
+
+ txtPassword.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ txtPasswordActionPerformed(evt);
+ }
+ });
+
lblFlag.setLabelFor(txtUserName);
lblFlag.setText("User flag:");
@@ -234,7 +254,8 @@ public class ConnectDialog extends MageDialog {
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(lblPort)
.addComponent(lblServer)
- .addComponent(lblUserName))
+ .addComponent(lblUserName)
+ .addComponent(lblPassword))
.addComponent(lblFlag, javax.swing.GroupLayout.Alignment.TRAILING))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@@ -245,7 +266,8 @@ public class ConnectDialog extends MageDialog {
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
.addComponent(txtServer, javax.swing.GroupLayout.DEFAULT_SIZE, 286, Short.MAX_VALUE)
.addComponent(txtPort, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 71, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addComponent(txtUserName))
+ .addComponent(txtUserName)
+ .addComponent(txtPassword))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnFind))
.addComponent(chkForceUpdateDB, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
@@ -269,6 +291,10 @@ public class ConnectDialog extends MageDialog {
.addComponent(txtUserName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lblUserName))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(txtPassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(lblPassword))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(lblFlag, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(cbFlag, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
@@ -302,10 +328,6 @@ public class ConnectDialog extends MageDialog {
private void btnConnectActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnConnectActionPerformed
- if (txtUserName.getText().isEmpty()) {
- JOptionPane.showMessageDialog(rootPane, "Please provide a user name");
- return;
- }
if (txtServer.getText().trim().isEmpty()) {
JOptionPane.showMessageDialog(rootPane, "Please provide a server address");
return;
@@ -314,6 +336,14 @@ public class ConnectDialog extends MageDialog {
JOptionPane.showMessageDialog(rootPane, "Please provide a port number");
return;
}
+ if (txtUserName.getText().isEmpty()) {
+ JOptionPane.showMessageDialog(rootPane, "Please provide a user name");
+ return;
+ }
+ if (txtPassword.getText().isEmpty()) {
+ JOptionPane.showMessageDialog(rootPane, "Please provide a password");
+ return;
+ }
if (Integer.valueOf(txtPort.getText()) < 1 || Integer.valueOf(txtPort.getText()) > 65535) {
JOptionPane.showMessageDialog(rootPane, "Invalid port number");
txtPort.setText(MageFrame.getPreferences().get("serverPort", Integer.toString(Config.port)));
@@ -327,6 +357,7 @@ public class ConnectDialog extends MageDialog {
connection.setHost(this.txtServer.getText().trim());
connection.setPort(Integer.valueOf(this.txtPort.getText().trim()));
connection.setUsername(this.txtUserName.getText().trim());
+ connection.setPassword(this.txtPassword.getText().trim());
connection.setForceDBComparison(this.chkForceUpdateDB.isSelected());
MageFrame.getPreferences().put(KEY_CONNECT_FLAG, ((CountryItemEditor) cbFlag.getEditor()).getImageItem());
@@ -541,6 +572,14 @@ public class ConnectDialog extends MageDialog {
// TODO add your handling code here:
}//GEN-LAST:event_chkForceUpdateDBActionPerformed
+ private void txtUserNameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_txtUserNameActionPerformed
+ // TODO add your handling code here:
+ }//GEN-LAST:event_txtUserNameActionPerformed
+
+ private void txtPasswordActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_txtPasswordActionPerformed
+ // TODO add your handling code here:
+ }//GEN-LAST:event_txtPasswordActionPerformed
+
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton btnCancel;
private javax.swing.JButton btnConnect;
@@ -550,10 +589,12 @@ public class ConnectDialog extends MageDialog {
private javax.swing.JCheckBox chkForceUpdateDB;
private javax.swing.JButton jProxySettingsButton;
private javax.swing.JLabel lblFlag;
+ private javax.swing.JLabel lblPassword;
private javax.swing.JLabel lblPort;
private javax.swing.JLabel lblServer;
private javax.swing.JLabel lblStatus;
private javax.swing.JLabel lblUserName;
+ private javax.swing.JPasswordField txtPassword;
private javax.swing.JTextField txtPort;
private javax.swing.JTextField txtServer;
private javax.swing.JTextField txtUserName;
diff --git a/Mage.Common/src/mage/interfaces/MageServer.java b/Mage.Common/src/mage/interfaces/MageServer.java
index 4d30942a1e..75baad0e6a 100644
--- a/Mage.Common/src/mage/interfaces/MageServer.java
+++ b/Mage.Common/src/mage/interfaces/MageServer.java
@@ -56,7 +56,7 @@ import mage.view.UserView;
public interface MageServer {
// connection methods
- boolean registerClient(String userName, String sessionId, MageVersion version) throws MageException;
+ boolean registerClient(String userName, String password, String sessionId, MageVersion version) throws MageException;
boolean registerAdmin(String password, String sessionId, MageVersion version) throws MageException;
// Not used
diff --git a/Mage.Common/src/mage/remote/Connection.java b/Mage.Common/src/mage/remote/Connection.java
index 0c4e86c827..4b68eb2560 100644
--- a/Mage.Common/src/mage/remote/Connection.java
+++ b/Mage.Common/src/mage/remote/Connection.java
@@ -45,6 +45,7 @@ public class Connection {
private int port;
private String username;
private String password;
+ private String adminPassword;
private ProxyType proxyType;
private String proxyHost;
private int proxyPort;
@@ -172,6 +173,14 @@ public class Connection {
this.password = password;
}
+ public String getAdminPassword() {
+ return adminPassword;
+ }
+
+ public void setAdminPassword(String adminPassword) {
+ this.adminPassword = adminPassword;
+ }
+
public String getProxyHost() {
return proxyHost;
}
diff --git a/Mage.Common/src/mage/remote/SessionImpl.java b/Mage.Common/src/mage/remote/SessionImpl.java
index 3b4036d4c2..a75ab5ab49 100644
--- a/Mage.Common/src/mage/remote/SessionImpl.java
+++ b/Mage.Common/src/mage/remote/SessionImpl.java
@@ -278,14 +278,14 @@ public class SessionImpl implements Session {
this.sessionId = callbackClient.getSessionId();
boolean registerResult;
- if (connection.getPassword() == null) {
+ if (connection.getAdminPassword() == null) {
// for backward compatibility. don't remove twice call - first one does nothing but for version checking
- registerResult = server.registerClient(connection.getUsername(), sessionId, client.getVersion());
+ registerResult = server.registerClient(connection.getUsername(), connection.getPassword(), sessionId, client.getVersion());
if (registerResult) {
server.setUserData(connection.getUsername(), sessionId, connection.getUserData());
}
} else {
- registerResult = server.registerAdmin(connection.getPassword(), sessionId, client.getVersion());
+ registerResult = server.registerAdmin(connection.getAdminPassword(), sessionId, client.getVersion());
}
if (registerResult) {
sessionState = SessionState.CONNECTED;
diff --git a/Mage.Server.Console/src/main/java/mage/server/console/ConnectDialog.java b/Mage.Server.Console/src/main/java/mage/server/console/ConnectDialog.java
index 127efc1b71..6a4b6e8620 100644
--- a/Mage.Server.Console/src/main/java/mage/server/console/ConnectDialog.java
+++ b/Mage.Server.Console/src/main/java/mage/server/console/ConnectDialog.java
@@ -413,7 +413,7 @@ public class ConnectDialog extends JDialog {
connection = new Connection();
connection.setHost(this.txtServer.getText());
connection.setPort(Integer.valueOf(this.txtPort.getText()));
- connection.setPassword(new String(txtPassword.getPassword()));
+ connection.setAdminPassword(new String(txtPassword.getPassword()));
connection.setUsername("Admin");
connection.setProxyType((ProxyType) this.cbProxyType.getSelectedItem());
if (!this.cbProxyType.getSelectedItem().equals(ProxyType.NONE)) {
diff --git a/Mage.Server/pom.xml b/Mage.Server/pom.xml
index dabb43df49..80916da83e 100644
--- a/Mage.Server/pom.xml
+++ b/Mage.Server/pom.xml
@@ -148,6 +148,12 @@
${project.version}
runtime
+
+ org.apache.shiro
+ shiro-core
+ 1.2.4
+ jar
+
diff --git a/Mage.Server/src/main/java/mage/server/AuthorizedUser.java b/Mage.Server/src/main/java/mage/server/AuthorizedUser.java
new file mode 100644
index 0000000000..d3a678ccd1
--- /dev/null
+++ b/Mage.Server/src/main/java/mage/server/AuthorizedUser.java
@@ -0,0 +1,52 @@
+package mage.server;
+
+import com.j256.ormlite.field.DatabaseField;
+import com.j256.ormlite.table.DatabaseTable;
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.SimpleAuthenticationInfo;
+import org.apache.shiro.authc.UsernamePasswordToken;
+import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
+import org.apache.shiro.util.ByteSource;
+import org.apache.shiro.codec.Base64;
+import org.apache.shiro.crypto.hash.Hash;
+
+@DatabaseTable(tableName = "authorized_user")
+public class AuthorizedUser {
+
+ @DatabaseField(indexName = "name_index")
+ protected String name;
+
+ @DatabaseField
+ protected String password;
+
+ @DatabaseField
+ protected String salt;
+
+ @DatabaseField
+ protected String hashAlgorithm;
+
+ @DatabaseField
+ protected int hashIterations;
+
+ public AuthorizedUser() {
+ }
+
+ public AuthorizedUser(String name, Hash hash) {
+ this.name = name;
+ this.password = hash.toBase64();
+ this.salt = hash.getSalt().toBase64();
+ this.hashAlgorithm = hash.getAlgorithmName();
+ this.hashIterations = hash.getIterations();
+ }
+
+ public boolean doCredentialsMatch(String name, String password) {
+ HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(this.hashAlgorithm);
+ matcher.setHashIterations(this.hashIterations);
+ AuthenticationToken token = new UsernamePasswordToken(name, password);
+ AuthenticationInfo info = new SimpleAuthenticationInfo(this.name,
+ ByteSource.Util.bytes(Base64.decode(this.password)),
+ ByteSource.Util.bytes(Base64.decode(this.salt)), "");
+ return matcher.doCredentialsMatch(token, info);
+ }
+}
diff --git a/Mage.Server/src/main/java/mage/server/AuthorizedUserRepository.java b/Mage.Server/src/main/java/mage/server/AuthorizedUserRepository.java
new file mode 100644
index 0000000000..551c8cb6fc
--- /dev/null
+++ b/Mage.Server/src/main/java/mage/server/AuthorizedUserRepository.java
@@ -0,0 +1,97 @@
+package mage.server;
+
+import com.j256.ormlite.dao.Dao;
+import com.j256.ormlite.dao.DaoManager;
+import com.j256.ormlite.jdbc.JdbcConnectionSource;
+import com.j256.ormlite.stmt.QueryBuilder;
+import com.j256.ormlite.stmt.SelectArg;
+import com.j256.ormlite.support.ConnectionSource;
+import com.j256.ormlite.support.DatabaseConnection;
+import com.j256.ormlite.table.TableUtils;
+import java.io.File;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.concurrent.Callable;
+import mage.cards.repository.RepositoryUtil;
+import org.apache.log4j.Logger;
+import org.apache.shiro.crypto.RandomNumberGenerator;
+import org.apache.shiro.crypto.SecureRandomNumberGenerator;
+import org.apache.shiro.crypto.hash.Hash;
+import org.apache.shiro.crypto.hash.Sha256Hash;
+import org.apache.shiro.crypto.hash.SimpleHash;
+
+public enum AuthorizedUserRepository {
+
+ instance;
+
+ private static final String JDBC_URL = "jdbc:h2:file:./db/authorized_user.h2;AUTO_SERVER=TRUE";
+ private static final String VERSION_ENTITY_NAME = "authorized_user";
+ // raise this if db structure was changed
+ private static final long DB_VERSION = 0;
+ private static final RandomNumberGenerator rng = new SecureRandomNumberGenerator();
+
+ private Dao dao;
+
+ private AuthorizedUserRepository() {
+ File file = new File("db");
+ if (!file.exists()) {
+ file.mkdirs();
+ }
+ try {
+ ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL);
+ boolean obsolete = RepositoryUtil.isDatabaseObsolete(connectionSource, VERSION_ENTITY_NAME, DB_VERSION);
+
+ if (obsolete) {
+ TableUtils.dropTable(connectionSource, AuthorizedUser.class, true);
+ }
+
+ TableUtils.createTableIfNotExists(connectionSource, AuthorizedUser.class);
+ dao = DaoManager.createDao(connectionSource, AuthorizedUser.class);
+ } catch (SQLException ex) {
+ Logger.getLogger(AuthorizedUserRepository.class).error("Error creating authorized_user repository - ", ex);
+ }
+ }
+
+ public void add(final String userName, final String password) {
+ try {
+ dao.callBatchTasks(new Callable