From a0ddd4fff0ae33a5243cd1d131adb4c90b041776 Mon Sep 17 00:00:00 2001 From: Me Car Date: Sun, 10 Jan 2016 18:08:08 +0900 Subject: [PATCH] Send an email on successful user registration. Use Gmail API for that. Add password confirmation and email to RegisterUserDialog. --- .../client/dialog/RegisterUserDialog.form | 108 ++++++++++++++---- .../client/dialog/RegisterUserDialog.java | 95 +++++++++++---- Mage.Common/src/mage/remote/Connection.java | 9 ++ Mage.Common/src/mage/remote/SessionImpl.java | 2 +- Mage.Server/config/config.xml | 2 + Mage.Server/pom.xml | 30 +++++ .../main/java/mage/server/GmailClient.java | 86 ++++++++++++++ .../src/main/java/mage/server/Main.java | 14 ++- .../src/main/java/mage/server/Session.java | 6 + .../java/mage/server/util/ConfigSettings.java | 16 ++- .../main/xml-resources/jaxb/Config/Config.xsd | 30 ++--- 11 files changed, 328 insertions(+), 70 deletions(-) create mode 100644 Mage.Server/src/main/java/mage/server/GmailClient.java diff --git a/Mage.Client/src/main/java/mage/client/dialog/RegisterUserDialog.form b/Mage.Client/src/main/java/mage/client/dialog/RegisterUserDialog.form index 5f99dece42..1b343dc991 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/RegisterUserDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/RegisterUserDialog.form @@ -24,36 +24,39 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - + - + @@ -80,14 +83,26 @@ - + + + + + + + + + + + + + - + - + @@ -157,5 +172,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mage.Client/src/main/java/mage/client/dialog/RegisterUserDialog.java b/Mage.Client/src/main/java/mage/client/dialog/RegisterUserDialog.java index 38d0756820..e6e989da65 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/RegisterUserDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/RegisterUserDialog.java @@ -56,6 +56,12 @@ public class RegisterUserDialog extends MageDialog { lblStatus = new javax.swing.JLabel(); txtServer = new javax.swing.JTextField(); txtPort = new javax.swing.JTextField(); + txtEmail = new javax.swing.JTextField(); + lblPasswordConfirmation = new javax.swing.JLabel(); + txtPasswordConfirmation = new javax.swing.JPasswordField(); + lblEmail = new javax.swing.JLabel(); + lblPasswordConfirmationReasoning = new javax.swing.JLabel(); + lblEmailReasoning = new javax.swing.JLabel(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); setTitle("Register"); @@ -94,34 +100,53 @@ public class RegisterUserDialog extends MageDialog { lblStatus.setToolTipText(""); + lblPasswordConfirmation.setLabelFor(txtPasswordConfirmation); + lblPasswordConfirmation.setText("Password:"); + + lblEmail.setLabelFor(txtEmail); + lblEmail.setText("Email:"); + + lblPasswordConfirmationReasoning.setFont(new java.awt.Font("Lucida Grande", 0, 10)); // NOI18N + lblPasswordConfirmationReasoning.setLabelFor(txtPasswordConfirmation); + lblPasswordConfirmationReasoning.setText("(confirmation)"); + + lblEmailReasoning.setFont(new java.awt.Font("Lucida Grande", 0, 10)); // NOI18N + lblEmailReasoning.setLabelFor(txtEmail); + lblEmailReasoning.setText("(used for password reset)"); + lblEmailReasoning.setToolTipText(""); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGap(14, 14, 14) - .addComponent(lblUserName)) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(lblPassword, javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(lblPort, javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(lblServer, javax.swing.GroupLayout.Alignment.TRAILING)))) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(lblStatus, javax.swing.GroupLayout.PREFERRED_SIZE, 292, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(lblPasswordConfirmationReasoning) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(lblServer) + .addComponent(lblUserName) + .addComponent(lblPort) + .addComponent(lblPassword) + .addComponent(lblPasswordConfirmation) + .addComponent(lblEmail)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(txtServer, javax.swing.GroupLayout.PREFERRED_SIZE, 292, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(txtPort, javax.swing.GroupLayout.PREFERRED_SIZE, 292, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(txtUserName, javax.swing.GroupLayout.PREFERRED_SIZE, 292, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(txtPassword, javax.swing.GroupLayout.PREFERRED_SIZE, 292, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(txtPasswordConfirmation, javax.swing.GroupLayout.PREFERRED_SIZE, 292, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(txtEmail, javax.swing.GroupLayout.PREFERRED_SIZE, 292, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addComponent(lblEmailReasoning) .addGroup(layout.createSequentialGroup() .addComponent(btnRegister) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(btnCancel)) - .addComponent(txtUserName, javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(txtPassword, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 292, Short.MAX_VALUE) - .addComponent(txtServer, javax.swing.GroupLayout.Alignment.LEADING)) - .addComponent(txtPort, javax.swing.GroupLayout.PREFERRED_SIZE, 292, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addContainerGap(16, Short.MAX_VALUE)) + .addComponent(btnCancel))) + .addComponent(lblStatus, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap(22, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -142,13 +167,24 @@ public class RegisterUserDialog extends MageDialog { .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(lblPassword) .addComponent(txtPassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGap(18, 18, 18) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(txtPasswordConfirmation, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lblPasswordConfirmation)) + .addComponent(lblPasswordConfirmationReasoning) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblEmail) + .addComponent(txtEmail, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lblEmailReasoning) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(lblStatus, javax.swing.GroupLayout.PREFERRED_SIZE, 28, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(btnRegister) - .addComponent(btnCancel)) - .addContainerGap(14, Short.MAX_VALUE)) + .addComponent(btnCancel) + .addComponent(btnRegister)) + .addContainerGap()) ); pack(); @@ -163,11 +199,16 @@ public class RegisterUserDialog extends MageDialog { }//GEN-LAST:event_btnCancelActionPerformed private void btnRegisterActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnRegisterActionPerformed + if (!this.txtPassword.getText().equals(this.txtPasswordConfirmation.getText())) { + MageFrame.getInstance().showError("Passwords don't match."); + return; + } connection = new Connection(); 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.setEmail(this.txtEmail.getText().trim()); PreferencesDialog.setProxyInformation(connection); task = new ConnectTask(); task.execute(); @@ -218,12 +259,18 @@ public class RegisterUserDialog extends MageDialog { // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton btnCancel; private javax.swing.JButton btnRegister; + private javax.swing.JLabel lblEmail; + private javax.swing.JLabel lblEmailReasoning; private javax.swing.JLabel lblPassword; + private javax.swing.JLabel lblPasswordConfirmation; + private javax.swing.JLabel lblPasswordConfirmationReasoning; private javax.swing.JLabel lblPort; private javax.swing.JLabel lblServer; private javax.swing.JLabel lblStatus; private javax.swing.JLabel lblUserName; + private javax.swing.JTextField txtEmail; private javax.swing.JPasswordField txtPassword; + private javax.swing.JPasswordField txtPasswordConfirmation; private javax.swing.JTextField txtPort; private javax.swing.JTextField txtServer; private javax.swing.JTextField txtUserName; diff --git a/Mage.Common/src/mage/remote/Connection.java b/Mage.Common/src/mage/remote/Connection.java index 4b68eb2560..818ef0b46f 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 email; private String adminPassword; private ProxyType proxyType; private String proxyHost; @@ -173,6 +174,14 @@ public class Connection { this.password = password; } + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + public String getAdminPassword() { return adminPassword; } diff --git a/Mage.Common/src/mage/remote/SessionImpl.java b/Mage.Common/src/mage/remote/SessionImpl.java index ddc6b99d87..5a5d393218 100644 --- a/Mage.Common/src/mage/remote/SessionImpl.java +++ b/Mage.Common/src/mage/remote/SessionImpl.java @@ -195,7 +195,7 @@ public class SessionImpl implements Session { public boolean run() throws Throwable { logger.info("Trying to register as " + getUserName() + " to XMAGE server at " + connection.getHost() + ":" + connection.getPort()); boolean registerResult = server.registerUser(sessionId, connection.getUsername(), - connection.getPassword(), ""); + connection.getPassword(), connection.getEmail()); if (registerResult) { logger.info("Registered as " + getUserName() + " to MAGE server at " + connection.getHost() + ":" + connection.getPort()); } diff --git a/Mage.Server/config/config.xml b/Mage.Server/config/config.xml index e2f7d4b11a..4070d1a7ea 100644 --- a/Mage.Server/config/config.xml +++ b/Mage.Server/config/config.xml @@ -35,6 +35,8 @@ userNamePattern="[^a-z0-9_]" maxAiOpponents="15" saveGameActivated="false" + authenticationActivated="false" + googleAccount="" /> diff --git a/Mage.Server/pom.xml b/Mage.Server/pom.xml index 80916da83e..a46f67e394 100644 --- a/Mage.Server/pom.xml +++ b/Mage.Server/pom.xml @@ -154,6 +154,36 @@ 1.2.4 jar + + com.google.api-client + google-api-client + 1.21.0 + jar + + + com.google.apis + google-api-services-gmail + v1-rev35-1.21.0 + jar + + + com.google.oauth-client + google-oauth-client-java6 + 1.19.0 + jar + + + com.google.oauth-client + google-oauth-client-jetty + 1.19.0 + jar + + + javax.mail + mail + 1.4.2 + jar + diff --git a/Mage.Server/src/main/java/mage/server/GmailClient.java b/Mage.Server/src/main/java/mage/server/GmailClient.java new file mode 100644 index 0000000000..45ef28225b --- /dev/null +++ b/Mage.Server/src/main/java/mage/server/GmailClient.java @@ -0,0 +1,86 @@ +package mage.server; + +import com.google.api.client.auth.oauth2.Credential; +import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp; +import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver; +import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow; +import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets; +import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.json.JsonFactory; +import com.google.api.client.json.jackson2.JacksonFactory; +import com.google.api.client.util.Base64; +import com.google.api.client.util.store.FileDataStoreFactory; +import com.google.api.services.gmail.Gmail; +import com.google.api.services.gmail.Gmail.Builder; +import com.google.api.services.gmail.GmailScopes; +import com.google.api.services.gmail.model.Message; +import java.io.ByteArrayOutputStream; +import java.io.FileReader; +import java.io.IOException; +import javax.mail.Session; +import java.security.GeneralSecurityException; +import java.util.Collections; +import java.util.Properties; +import javax.mail.MessagingException; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; +import mage.server.util.ConfigSettings; +import org.apache.log4j.Logger; + +public class GmailClient { + + private static final Logger logger = Logger.getLogger(Main.class); + private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance(); + private static final java.io.File DATA_STORE_DIR = new java.io.File(System.getProperty("user.home"), ".store/xmage"); + private static FileDataStoreFactory dataStoreFactory; + private static HttpTransport httpTransport; + private static Credential credential; + + public static boolean initilize() { + try { + dataStoreFactory = new FileDataStoreFactory(DATA_STORE_DIR); + httpTransport = GoogleNetHttpTransport.newTrustedTransport(); + + GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new FileReader("client_secrets.json")); + if (clientSecrets.getDetails().getClientId().startsWith("Enter") + || clientSecrets.getDetails().getClientSecret().startsWith("Enter ")) { + logger.error("client_secrets.json not found"); + return false; + } + + GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder( + httpTransport, JSON_FACTORY, clientSecrets, + Collections.singleton(GmailScopes.GMAIL_COMPOSE)).setDataStoreFactory( + dataStoreFactory).build(); + + credential = new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user"); + return true; + } catch (IOException | GeneralSecurityException ex) { + logger.error("Error initializing GmailClient", ex); + } + return false; + } + + public static boolean sendMessage(String email, String subject, String text) { + try { + Gmail gmail = new Builder(httpTransport, JSON_FACTORY, credential).setApplicationName("XMage Server").build(); + + MimeMessage mimeMessage = new MimeMessage(Session.getDefaultInstance(new Properties())); + mimeMessage.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(email)); + mimeMessage.setSubject(subject); + mimeMessage.setText(text); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + mimeMessage.writeTo(baos); + Message message = new Message(); + message.setRaw(Base64.encodeBase64URLSafeString(baos.toByteArray())); + + gmail.users().messages().send(ConfigSettings.getInstance().getGoogleAccount() + "@gmail.com", message).execute(); + return true; + } catch (MessagingException | IOException ex) { + logger.error("Error sending message", ex); + } + return false; + } +} diff --git a/Mage.Server/src/main/java/mage/server/Main.java b/Mage.Server/src/main/java/mage/server/Main.java index 7e83044274..781eb01281 100644 --- a/Mage.Server/src/main/java/mage/server/Main.java +++ b/Mage.Server/src/main/java/mage/server/Main.java @@ -107,7 +107,18 @@ public class Main { fastDbMode = Boolean.valueOf(arg.replace(fastDBModeArg, "")); } } - + + ConfigSettings config = ConfigSettings.getInstance(); + if (config.isAuthenticationActivated()) { + logger.info("Initializing GmailClient. This will open up a tab in your browser to ask for an OAuth access token."); + if (GmailClient.initilize()) { + logger.info("GmailClient initilized successfully."); + } else { + logger.fatal("GmailClient initialization failed."); + return; + } + } + logger.info("Loading cards..."); if (fastDbMode) { CardScanner.scanned = true; @@ -117,7 +128,6 @@ public class Main { logger.info("Done."); deleteSavedGames(); - ConfigSettings config = ConfigSettings.getInstance(); for (GamePlugin plugin: config.getGameTypes()) { GameFactory.getInstance().addGameType(plugin.getName(), loadGameType(plugin), loadPlugin(plugin)); } diff --git a/Mage.Server/src/main/java/mage/server/Session.java b/Mage.Server/src/main/java/mage/server/Session.java index d57d710fc9..9bddf546bd 100644 --- a/Mage.Server/src/main/java/mage/server/Session.java +++ b/Mage.Server/src/main/java/mage/server/Session.java @@ -87,6 +87,12 @@ public class Session { return returnMessage; } AuthorizedUserRepository.instance.add(userName, password, email); + if (GmailClient.sendMessage(email, "XMage Registration Completed", + "You are successfully registered as " + userName + ".")) { + logger.info("Sent a registration confirmation email to " + email + " for " + userName); + } else { + logger.error("Failed sending a registration confirmation email to " + email + " for " + userName); + } return null; } } diff --git a/Mage.Server/src/main/java/mage/server/util/ConfigSettings.java b/Mage.Server/src/main/java/mage/server/util/ConfigSettings.java index cee5d5bdee..c85f97c257 100644 --- a/Mage.Server/src/main/java/mage/server/util/ConfigSettings.java +++ b/Mage.Server/src/main/java/mage/server/util/ConfigSettings.java @@ -94,7 +94,7 @@ public class ConfigSettings { public int getBacklogSize() { return config.getServer().getBacklogSize().intValue(); } - + public int getMaxGameThreads() { return config.getServer().getMaxGameThreads().intValue(); } @@ -114,14 +114,22 @@ public class ConfigSettings { public String getUserNamePattern() { return config.getServer().getUserNamePattern(); } - + public String getMaxAiOpponents() { return config.getServer().getMaxAiOpponents(); } public Boolean isSaveGameActivated() { - return config.getServer().isSaveGameActivated(); -} + return config.getServer().isSaveGameActivated(); + } + + public Boolean isAuthenticationActivated() { + return config.getServer().isAuthenticationActivated(); + } + + public String getGoogleAccount() { + return config.getServer().getGoogleAccount(); + } public List getPlayerTypes() { return config.getPlayerTypes().getPlayerType(); diff --git a/Mage.Server/src/main/xml-resources/jaxb/Config/Config.xsd b/Mage.Server/src/main/xml-resources/jaxb/Config/Config.xsd index 8e57894338..25adcc1e0e 100644 --- a/Mage.Server/src/main/xml-resources/jaxb/Config/Config.xsd +++ b/Mage.Server/src/main/xml-resources/jaxb/Config/Config.xsd @@ -2,19 +2,19 @@ - + - + - + @@ -22,16 +22,18 @@ - - - - - - - - - - + + + + + + + + + + + + @@ -56,7 +58,7 @@ - +