diff --git a/platform/platform-api/src/com/intellij/credentialStore/CredentialAttributes.kt b/platform/platform-api/src/com/intellij/credentialStore/CredentialAttributes.kt index 2a9345c7f485..27d55c4e8ff0 100644 --- a/platform/platform-api/src/com/intellij/credentialStore/CredentialAttributes.kt +++ b/platform/platform-api/src/com/intellij/credentialStore/CredentialAttributes.kt @@ -29,7 +29,7 @@ import java.util.concurrent.atomic.AtomicReference /** * requestor is deprecated. Never use it in new code. */ -data class CredentialAttributes(val serviceName: String, val userName: String? = null, val requestor: Class<*>? = null) { +data class CredentialAttributes @JvmOverloads constructor(val serviceName: String, val userName: String? = null, val requestor: Class<*>? = null) { } // user cannot be empty, but password can be @@ -38,6 +38,8 @@ class Credentials(user: String?, val password: OneTimeString? = null) { constructor(user: String?, password: CharArray?) : this(user, password?.let { OneTimeString(it) }) + constructor(user: String?, password: ByteArray?) : this(user, password?.let { OneTimeString(password) }) + val userName = user.nullize() fun getPasswordAsString() = password?.toString() diff --git a/platform/platform-impl/src/com/intellij/diagnostic/ErrorReportConfigurable.java b/platform/platform-impl/src/com/intellij/diagnostic/ErrorReportConfigurable.java index 3f865dd19abd..e738a646c5e3 100644 --- a/platform/platform-impl/src/com/intellij/diagnostic/ErrorReportConfigurable.java +++ b/platform/platform-impl/src/com/intellij/diagnostic/ErrorReportConfigurable.java @@ -15,58 +15,43 @@ */ package com.intellij.diagnostic; -import com.intellij.openapi.components.NamedComponent; -import com.intellij.openapi.components.ServiceManager; -import com.intellij.openapi.util.DefaultJDOMExternalizer; -import com.intellij.openapi.util.InvalidDataException; -import com.intellij.openapi.util.JDOMExternalizable; -import com.intellij.openapi.util.WriteExternalException; -import com.intellij.openapi.vfs.CharsetToolkit; -import com.intellij.util.Base64; -import org.jdom.Element; -import org.jetbrains.annotations.NotNull; +import com.intellij.credentialStore.CredentialAttributes; +import com.intellij.credentialStore.Credentials; +import com.intellij.ide.passwordSafe.PasswordSafe; +import com.intellij.openapi.components.*; +import com.intellij.openapi.util.text.StringUtil; +import org.jetbrains.annotations.Nullable; -public class ErrorReportConfigurable implements JDOMExternalizable, NamedComponent { - public String ITN_LOGIN = ""; - public String ITN_PASSWORD_CRYPT = ""; - public boolean KEEP_ITN_PASSWORD = false; +import java.util.Base64; - public String EMAIL = ""; +@State(name = "ErrorReportConfigurable", storages = @Storage(value = "other.xml", deprecated = true, roamingType = RoamingType.DISABLED)) +class ErrorReportConfigurable implements PersistentStateComponent { + public static final String SERVICE_NAME = "IntelliJ Platform — JetBrains Account"; + + static class State { + public String ITN_LOGIN; + public String ITN_PASSWORD_CRYPT; + } public static ErrorReportConfigurable getInstance() { return ServiceManager.getService(ErrorReportConfigurable.class); } + @Nullable @Override - public void readExternal(Element element) throws InvalidDataException { - DefaultJDOMExternalizer.readExternal(this, element); - if (!KEEP_ITN_PASSWORD) { - ITN_PASSWORD_CRYPT = ""; + public State getState() { + return new State(); + } + + @Override + public void loadState(State state) { + if (!StringUtil.isEmpty(state.ITN_LOGIN) || !StringUtil.isEmpty(state.ITN_PASSWORD_CRYPT)) { + PasswordSafe.getInstance().set(new CredentialAttributes(SERVICE_NAME, state.ITN_LOGIN), new Credentials(state.ITN_LOGIN, Base64.getDecoder().decode(state.ITN_PASSWORD_CRYPT))); } } - @Override - public void writeExternal(Element element) throws WriteExternalException { - String itnPassword = ITN_PASSWORD_CRYPT; - if (!KEEP_ITN_PASSWORD) { - ITN_PASSWORD_CRYPT = ""; - } - DefaultJDOMExternalizer.writeExternal(this, element); - - ITN_PASSWORD_CRYPT = itnPassword; - } - - @Override - @NotNull - public String getComponentName() { - return "ErrorReportConfigurable"; - } - - public String getPlainItnPassword() { - return new String(Base64.decode(getInstance().ITN_PASSWORD_CRYPT), CharsetToolkit.UTF8_CHARSET); - } - - public void setPlainItnPassword(String password) { - ITN_PASSWORD_CRYPT = Base64.encode(password.getBytes(CharsetToolkit.UTF8_CHARSET)); + @Nullable + public static Credentials getCredentials() { + return PasswordSafe.getInstance().get(new CredentialAttributes(SERVICE_NAME)); } } diff --git a/platform/platform-impl/src/com/intellij/diagnostic/ITNReporter.java b/platform/platform-impl/src/com/intellij/diagnostic/ITNReporter.java index 85dd18e9add8..4fae72516f46 100644 --- a/platform/platform-impl/src/com/intellij/diagnostic/ITNReporter.java +++ b/platform/platform-impl/src/com/intellij/diagnostic/ITNReporter.java @@ -16,6 +16,8 @@ package com.intellij.diagnostic; import com.intellij.CommonBundle; +import com.intellij.credentialStore.CredentialAttributesKt; +import com.intellij.credentialStore.Credentials; import com.intellij.errorreport.bean.ErrorBean; import com.intellij.errorreport.error.InternalEAPException; import com.intellij.errorreport.error.NoSuchEAPUserException; @@ -28,7 +30,6 @@ import com.intellij.idea.IdeaLogger; import com.intellij.notification.NotificationListener; import com.intellij.notification.NotificationType; import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.ErrorReportSubmitter; import com.intellij.openapi.diagnostic.IdeaLoggingEvent; @@ -77,15 +78,13 @@ public class ITNReporter extends ErrorReportSubmitter { final Consumer callback, final ErrorBean errorBean, final String description) { - final DataContext dataContext = DataManager.getInstance().getDataContext(parentComponent); - final Project project = CommonDataKeys.PROJECT.getData(dataContext); - - ErrorReportConfigurable settings = ErrorReportConfigurable.getInstance(); - if (!settings.KEEP_ITN_PASSWORD && !StringUtil.isEmpty(settings.ITN_LOGIN) && StringUtil.isEmpty(settings.getPlainItnPassword())) { - JetBrainsAccountDialog dlg = new JetBrainsAccountDialog(parentComponent); - if (!dlg.showAndGet()) { + Credentials credentials = ErrorReportConfigurable.getCredentials(); + if (!CredentialAttributesKt.isFulfilled(credentials)) { + if (!new JetBrainsAccountDialog(parentComponent).showAndGet()) { return false; } + + credentials = ErrorReportConfigurable.getCredentials(); } errorBean.setDescription(description); @@ -95,17 +94,7 @@ public class ITNReporter extends ErrorReportSubmitter { errorBean.setPreviousException(previousExceptionThreadId); } - Throwable t = event.getThrowable(); - if (t != null) { - final PluginId pluginId = IdeErrorsDialog.findPluginId(t); - if (pluginId != null) { - final IdeaPluginDescriptor ideaPluginDescriptor = PluginManager.getPlugin(pluginId); - if (ideaPluginDescriptor != null && (!ideaPluginDescriptor.isBundled() || ideaPluginDescriptor.allowBundledUpdate())) { - errorBean.setPluginName(ideaPluginDescriptor.getName()); - errorBean.setPluginVersion(ideaPluginDescriptor.getVersion()); - } - } - } + setPluginInfo(event, errorBean); Object data = event.getData(); if (data instanceof AbstractMessage) { @@ -113,13 +102,14 @@ public class ITNReporter extends ErrorReportSubmitter { errorBean.setAttachments(((AbstractMessage)data).getIncludedAttachments()); } - String login = settings.ITN_LOGIN; - String password = settings.getPlainItnPassword(); + String login = credentials == null ? null : credentials.getUserName(); + String password = credentials == null ? null : credentials.getPasswordAsString(); if (StringUtil.isEmptyOrSpaces(login) && StringUtil.isEmptyOrSpaces(password)) { login = "idea_anonymous"; password = "guest"; } + Project project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(parentComponent)); ITNProxy.sendError(project, login, password, errorBean, threadId -> { updatePreviousThreadId(threadId); String url = ITNProxy.getBrowseUrl(threadId); @@ -175,6 +165,20 @@ public class ITNReporter extends ErrorReportSubmitter { return true; } + public static void setPluginInfo(IdeaLoggingEvent event, ErrorBean errorBean) { + Throwable t = event.getThrowable(); + if (t != null) { + final PluginId pluginId = IdeErrorsDialog.findPluginId(t); + if (pluginId != null) { + final IdeaPluginDescriptor ideaPluginDescriptor = PluginManager.getPlugin(pluginId); + if (ideaPluginDescriptor != null && (!ideaPluginDescriptor.isBundled() || ideaPluginDescriptor.allowBundledUpdate())) { + errorBean.setPluginName(ideaPluginDescriptor.getName()); + errorBean.setPluginVersion(ideaPluginDescriptor.getVersion()); + } + } + } + } + private static void updatePreviousThreadId(Integer threadId) { previousExceptionThreadId = threadId; } diff --git a/platform/platform-impl/src/com/intellij/diagnostic/IdeErrorsDialog.java b/platform/platform-impl/src/com/intellij/diagnostic/IdeErrorsDialog.java index bab7901c42af..739db6876806 100644 --- a/platform/platform-impl/src/com/intellij/diagnostic/IdeErrorsDialog.java +++ b/platform/platform-impl/src/com/intellij/diagnostic/IdeErrorsDialog.java @@ -17,6 +17,8 @@ package com.intellij.diagnostic; import com.intellij.CommonBundle; import com.intellij.ExtensionPoints; +import com.intellij.credentialStore.CredentialAttributesKt; +import com.intellij.credentialStore.Credentials; import com.intellij.diagnostic.errordialog.*; import com.intellij.icons.AllIcons; import com.intellij.ide.DataManager; @@ -52,7 +54,6 @@ import com.intellij.openapi.wm.IdeFrame; import com.intellij.ui.HeaderlessTabbedPane; import com.intellij.ui.HyperlinkLabel; import com.intellij.ui.IdeBorderFactory; -import com.intellij.util.Consumer; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.text.DateFormatUtil; import com.intellij.util.ui.UIUtil; @@ -71,6 +72,7 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.math.BigInteger; +import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.*; @@ -80,7 +82,7 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene private static final Logger LOG = Logger.getInstance(IdeErrorsDialog.class.getName()); private final boolean myInternalMode; @NonNls private static final String ACTIVE_TAB_OPTION = IdeErrorsDialog.class.getName() + "activeTab"; - public static DataKey CURRENT_TRACE_KEY = DataKey.create("current_stack_trace_key"); + public static final DataKey CURRENT_TRACE_KEY = DataKey.create("current_stack_trace_key"); public static final int COMPONENTS_WIDTH = 670; public static Collection ourDevelopersList = Collections.emptyList(); @@ -287,7 +289,7 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene } }; if (!myInternalMode) { - myDetailsTabForm = new DetailsTabForm(null, myInternalMode); + myDetailsTabForm = new DetailsTabForm(null, false); myCommentsTabForm = new CommentsTabForm(); myCommentsTabForm.addCommentsListener(commentsListener); myTabs.addTab(DiagnosticBundle.message("error.comments.tab.title"), myCommentsTabForm.getContentPane()); @@ -298,7 +300,7 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene if (analyzePlatformAction != null) { myAnalyzeAction = new AnalyzeAction(analyzePlatformAction); } - myDetailsTabForm = new DetailsTabForm(myAnalyzeAction, myInternalMode); + myDetailsTabForm = new DetailsTabForm(myAnalyzeAction, true); myDetailsTabForm.setCommentsAreaVisible(true); myDetailsTabForm.addCommentsListener(commentsListener); } @@ -465,15 +467,16 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene private void updateCredentialsPane(AbstractMessage message) { if (message != null) { - final ErrorReportSubmitter submitter = getSubmitter(message.getThrowable()); + ErrorReportSubmitter submitter = getSubmitter(message.getThrowable()); if (submitter instanceof ITNReporter) { myCredentialsPanel.setVisible(true); - String userName = ErrorReportConfigurable.getInstance().ITN_LOGIN; - if (StringUtil.isEmpty(userName)) { - myCredentialsLabel.setHtmlText(DiagnosticBundle.message("diagnostic.error.report.submit.error.anonymously")); + Credentials credentials = ErrorReportConfigurable.getCredentials(); + if (CredentialAttributesKt.isFulfilled(credentials)) { + assert credentials != null; + myCredentialsLabel.setHtmlText(DiagnosticBundle.message("diagnostic.error.report.submit.report.as", credentials.getUserName())); } else { - myCredentialsLabel.setHtmlText(DiagnosticBundle.message("diagnostic.error.report.submit.report.as", userName)); + myCredentialsLabel.setHtmlText(DiagnosticBundle.message("diagnostic.error.report.submit.error.anonymously")); } return; } @@ -647,7 +650,7 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene myDetailsTabForm.setAssigneeId(message == null ? null : message.getAssigneeId()); - List attachments = message != null ? message.getAllAttachments() : Collections.emptyList(); + List attachments = message != null ? message.getAllAttachments() : Collections.emptyList(); if (!attachments.isEmpty()) { if (myTabs.indexOfComponent(myAttachmentsTabForm.getContentPane()) == -1) { myTabs.addTab(DiagnosticBundle.message("error.attachments.tab.title"), myAttachmentsTabForm.getContentPane()); @@ -672,7 +675,7 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene return throwable.getMessage(); } else { - return new StringBuffer().append(message.getMessage()).append("\n").append(message.getThrowableText()).toString(); + return message.getMessage() + "\n" + message.getThrowableText(); } } @@ -767,8 +770,8 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene } } - if (PluginManager.isPluginClass(className)) { - return PluginManager.getPluginByClassName(className); + if (PluginManagerCore.isPluginClass(className)) { + return PluginManagerCore.getPluginByClassName(className); } } } @@ -777,8 +780,8 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene if (t.getMessage() != null) { String className = t.getMessage(); - if (PluginManager.isPluginClass(className)) { - return PluginManager.getPluginByClassName(className); + if (PluginManagerCore.isPluginClass(className)) { + return PluginManagerCore.getPluginByClassName(className); } } } @@ -791,8 +794,8 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene pos = s.lastIndexOf('.'); if (pos >= 0) { s = s.substring(0, pos); - if (PluginManager.isPluginClass(s)) { - return PluginManager.getPluginByClassName(s); + if (PluginManagerCore.isPluginClass(s)) { + return PluginManagerCore.getPluginByClassName(s); } } } @@ -800,8 +803,8 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene else if (t instanceof ExtensionException) { String className = ((ExtensionException)t).getExtensionClass().getName(); - if (PluginManager.isPluginClass(className)) { - return PluginManager.getPluginByClassName(className); + if (PluginManagerCore.isPluginClass(className)) { + return PluginManagerCore.getPluginByClassName(className); } } @@ -1016,8 +1019,8 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene private static String md5(String buffer, @NonNls String key) throws NoSuchAlgorithmException { MessageDigest md5 = MessageDigest.getInstance("MD5"); - md5.update(buffer.getBytes()); - byte[] code = md5.digest(key.getBytes()); + md5.update(buffer.getBytes(StandardCharsets.UTF_8)); + byte[] code = md5.digest(key.getBytes(StandardCharsets.UTF_8)); BigInteger bi = new BigInteger(code).abs(); return bi.abs().toString(16); } diff --git a/platform/platform-impl/src/com/intellij/diagnostic/JetBrainsAccountDialog.java b/platform/platform-impl/src/com/intellij/diagnostic/JetBrainsAccountDialog.java index fb34ba2342cf..d8d04b8b88bc 100644 --- a/platform/platform-impl/src/com/intellij/diagnostic/JetBrainsAccountDialog.java +++ b/platform/platform-impl/src/com/intellij/diagnostic/JetBrainsAccountDialog.java @@ -15,9 +15,13 @@ */ package com.intellij.diagnostic; +import com.intellij.credentialStore.CredentialAttributes; +import com.intellij.credentialStore.Credentials; import com.intellij.ide.BrowserUtil; +import com.intellij.ide.passwordSafe.PasswordSafe; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.ClickListener; import com.intellij.util.net.HttpConfigurable; import org.jetbrains.annotations.NotNull; @@ -28,20 +32,8 @@ import java.awt.event.MouseEvent; public class JetBrainsAccountDialog extends DialogWrapper { private JTextField myItnLoginTextField; - private JPasswordField myItnPasswordTextField; - private JCheckBox myRememberITNPasswordCheckBox; - - public void storeInfo() { - ErrorReportConfigurable.getInstance().ITN_LOGIN = myItnLoginTextField.getText(); - ErrorReportConfigurable.getInstance().setPlainItnPassword(new String(myItnPasswordTextField.getPassword())); - ErrorReportConfigurable.getInstance().KEEP_ITN_PASSWORD = myRememberITNPasswordCheckBox.isSelected(); - } - - public void loadInfo() { - myItnLoginTextField.setText(ErrorReportConfigurable.getInstance().ITN_LOGIN); - myItnPasswordTextField.setText(ErrorReportConfigurable.getInstance().getPlainItnPassword()); - myRememberITNPasswordCheckBox.setSelected(ErrorReportConfigurable.getInstance().KEEP_ITN_PASSWORD); - } + private JPasswordField myPasswordText; + private JCheckBox myRememberCheckBox; public JetBrainsAccountDialog(Component parent) throws HeadlessException { super(parent, false); @@ -82,7 +74,13 @@ public class JetBrainsAccountDialog extends DialogWrapper { mySendingSettingsLabel.setCursor(new Cursor(Cursor.HAND_CURSOR)); - loadInfo(); + Credentials credentials = ErrorReportConfigurable.getCredentials(); + String userName = credentials == null ? null : credentials.getUserName(); + myItnLoginTextField.setText(userName); + String password = credentials == null ? null : credentials.getPasswordAsString(); + myPasswordText.setText(password); + // if no user name - never stored and so, defaults to remember. if user name set, but no password, so, previously was stored without password + myRememberCheckBox.setSelected(StringUtil.isEmpty(userName) || !StringUtil.isEmpty(password)); new ClickListener() { @Override @@ -98,7 +96,11 @@ public class JetBrainsAccountDialog extends DialogWrapper { @Override protected void doOKAction() { - storeInfo(); + String userName = myItnLoginTextField.getText(); + if (!StringUtil.isEmpty(userName)) { + PasswordSafe.getInstance().set(new CredentialAttributes(ErrorReportConfigurable.SERVICE_NAME, userName), + new Credentials(userName, myRememberCheckBox.isSelected() ? myPasswordText.getPassword() : null)); + } super.doOKAction(); } diff --git a/platform/platform-impl/src/com/intellij/diagnostic/JetBrainsAccountForm.form b/platform/platform-impl/src/com/intellij/diagnostic/JetBrainsAccountForm.form index c5f3697acb11..e6fc473ec7d1 100644 --- a/platform/platform-impl/src/com/intellij/diagnostic/JetBrainsAccountForm.form +++ b/platform/platform-impl/src/com/intellij/diagnostic/JetBrainsAccountForm.form @@ -8,7 +8,7 @@ - + @@ -17,55 +17,11 @@ - + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -73,12 +29,46 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +