diff options
author | mattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-06 21:18:07 +0000 |
---|---|---|
committer | mattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-06 21:18:07 +0000 |
commit | 06dc32060d502c4341f61847f9f6054a110e15d5 (patch) | |
tree | da4c46e0be11deeb3263c255ae00ce8af6effa7f /chrome | |
parent | cc965a7964fbe747a3d8b9de9cd423fa8d13bbfc (diff) | |
download | chromium_src-06dc32060d502c4341f61847f9f6054a110e15d5.zip chromium_src-06dc32060d502c4341f61847f9f6054a110e15d5.tar.gz chromium_src-06dc32060d502c4341f61847f9f6054a110e15d5.tar.bz2 |
DOMUI Certificate Manager: Backup and restore PKCS #12 files. Export certs. Delete certs.
BUG=19991
TEST=manual
Review URL: http://codereview.chromium.org/3556007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@61701 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/app/generated_resources.grd | 75 | ||||
-rw-r--r-- | chrome/browser/certificate_manager_model.cc | 28 | ||||
-rw-r--r-- | chrome/browser/certificate_manager_model.h | 30 | ||||
-rw-r--r-- | chrome/browser/dom_ui/dom_ui.cc | 6 | ||||
-rw-r--r-- | chrome/browser/dom_ui/dom_ui.h | 2 | ||||
-rw-r--r-- | chrome/browser/dom_ui/options/certificate_manager_handler.cc | 460 | ||||
-rw-r--r-- | chrome/browser/dom_ui/options/certificate_manager_handler.h | 85 | ||||
-rw-r--r-- | chrome/browser/gtk/certificate_dialogs.cc | 6 | ||||
-rw-r--r-- | chrome/browser/resources/options.html | 10 | ||||
-rw-r--r-- | chrome/browser/resources/options/certificate_backup_overlay.html | 42 | ||||
-rw-r--r-- | chrome/browser/resources/options/certificate_backup_overlay.js | 118 | ||||
-rw-r--r-- | chrome/browser/resources/options/certificate_manager.html | 51 | ||||
-rw-r--r-- | chrome/browser/resources/options/certificate_manager.js | 88 | ||||
-rw-r--r-- | chrome/browser/resources/options/certificate_restore_overlay.html | 19 | ||||
-rw-r--r-- | chrome/browser/resources/options/certificate_restore_overlay.js | 97 |
15 files changed, 1087 insertions, 30 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index c9212b4..d05c9cf 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -3154,6 +3154,81 @@ each locale. --> <message name="IDS_CERT_MANAGER_VIEW_CERT_BUTTON" desc="Label for the button in the certificate manager which launches the certificate viewer for the selected certificate"> View </message> + <message name="IDS_CERT_MANAGER_IMPORT_BUTTON" desc="Label for the button in the certificate manager which allows you to import certificates"> + Import... + </message> + <message name="IDS_CERT_MANAGER_EXPORT_BUTTON" desc="Label for the button in the certificate manager which allows you to export certificates"> + Export... + </message> + <message name="IDS_CERT_MANAGER_EXPORT_ALL_BUTTON" desc="Label for the button in the certificate manager which saves all personal certificates and their private keys"> + Export All... + </message> + <message name="IDS_CERT_MANAGER_EDIT_BUTTON" desc="Label for the button in the certificate manager which allows you to edit certificate trust"> + Edit... + </message> + <message name="IDS_CERT_MANAGER_DELETE_BUTTON" desc="Label for the button in the certificate manager which allows you to delete certificates"> + Delete... + </message> + <message name="IDS_CERT_MANAGER_DELETE_USER_FORMAT" desc="Confirmation in certificate manager dialog for deleting a user certificate"> + Delete certificate "<ph name="CERTIFICATE_NAME">$1<ex>VeriSign Browser Certificate</ex></ph>"? + </message> + <message name="IDS_CERT_MANAGER_DELETE_USER_DESCRIPTION" desc="Description of impact of deleting a user certificate"> + If you delete one of your own certificates, you can no longer use it to identify yourself. + </message> + <message name="IDS_CERT_MANAGER_DELETE_SERVER_FORMAT" desc="Confirmation in certificate manager dialog for deleting a server certificate"> + Delete server certificate "<ph name="CERTIFICATE_NAME">$1<ex>www.example.com</ex></ph>"? + </message> + <message name="IDS_CERT_MANAGER_DELETE_SERVER_DESCRIPTION" desc="Description of impact of deleting a server certificate"> + If you delete a server certificate, you restore the usual security checks for that server and require it uses a valid certificate. + </message> + <message name="IDS_CERT_MANAGER_DELETE_CA_FORMAT" desc="Confirmation in certificate manager dialog for deleting a certificate authority certificate"> + Delete CA certificate "<ph name="CERTIFICATE_NAME">$1<ex>Verisign Class 1 Public Primary Certification Authority</ex></ph>"? + </message> + <message name="IDS_CERT_MANAGER_DELETE_CA_DESCRIPTION" desc="Description of impact of deleting a certificate authority certificate"> + If you delete a Certificate Authority (CA) certificate, your browser will no longer trust any certificates issued by that CA. + </message> + <message name="IDS_CERT_MANAGER_DELETE_UNKNOWN_FORMAT" desc="Confirmation in certificate manager dialog for deleting a certificate of other type"> + Delete certificate "<ph name="CERTIFICATE_NAME">$1<ex>Example Certificate</ex></ph>"? + </message> + <message name="IDS_CERT_MANAGER_EXPORT_PASSWORD_DESC" desc="Description of password prompt in certificate manager for exporting a personal certificate"> + Please enter a password to encrypt this certificate file. + </message> + <message name="IDS_CERT_MANAGER_EXPORT_PASSWORD_HELP" desc="Text in password prompt of certificate manager for exporting a personal certificate, reminding user that if they forget the password they're screwed."> + The password you choose will be required to restore this file later. Please record it in a safe location. + </message> + <message name="IDS_CERT_MANAGER_RESTORE_PASSWORD_DESC" desc="Description of password prompt in certificate manager for restoring a personal certificate"> + Please enter the password that was used to encrypt this certificate file. + </message> + <message name="IDS_CERT_MANAGER_PASSWORD_LABEL" desc="The label of the password field in the certificate manager for restoring or exporting a personal certificate"> + Password: + </message> + <message name="IDS_CERT_MANAGER_CONFIRM_PASSWORD_LABEL" desc="The label of the field in the certificate manager for re-entering the password when creating a export."> + Confirm Password: + </message> + <message name="IDS_CERT_MANAGER_PKCS12_FILES" desc="The label in the file selector dialog for PKCS #12 file type."> + PKCS #12 Files + </message> + <message name="IDS_CERT_MANAGER_PKCS12_EXPORT_ERROR_TITLE" desc="The title in the error dialog for PKCS #12 file export errors."> + PKCS #12 Export Error + </message> + <message name="IDS_CERT_MANAGER_WRITE_ERROR_FORMAT" desc="The text in the error dialog for PKCS #12 file write errors."> + There was an error while trying to write the file: <ph name="ERROR_TEXT">$1<ex>Permission denied.</ex></ph>. + </message> + <message name="IDS_CERT_MANAGER_PKCS12_IMPORT_ERROR_TITLE" desc="The title in the error dialog for PKCS #12 file import errors."> + PKCS #12 Import Error + </message> + <message name="IDS_CERT_MANAGER_READ_ERROR_FORMAT" desc="The text in the error dialog for PKCS #12 file read errors."> + There was an error while trying to read the file: <ph name="ERROR_TEXT">$1<ex>File not found.</ex></ph>. + </message> + <message name="IDS_CERT_MANAGER_BAD_PASSWORD" desc="The text in the error dialog for entering an incorrect password when importing an encrypted certificate file."> + Incorrect password. + </message> + <message name="IDS_CERT_MANAGER_UNKNOWN_ERROR" desc="The text in the error dialog when an unknown error occurs during an operation on the certificate database."> + Unknown error. + </message> + <message name="IDS_CERT_MANAGER_DELETE_CERT_ERROR_TITLE" desc="The title in the error dialog for certificate delete errors."> + Certificate Deletion Error + </message> <!-- Add Client Certificate Dialog --> <message name="IDS_ADD_CERT_DIALOG_ADD" desc="Title of the button that adds the certificate in the Add Client Certificate dialog"> diff --git a/chrome/browser/certificate_manager_model.cc b/chrome/browser/certificate_manager_model.cc index 170bc7a..4cdea8b184 100644 --- a/chrome/browser/certificate_manager_model.cc +++ b/chrome/browser/certificate_manager_model.cc @@ -11,6 +11,7 @@ #include "base/utf_string_conversions.h" #include "chrome/third_party/mozilla_security_manager/nsNSSCertHelper.h" #include "chrome/third_party/mozilla_security_manager/nsNSSCertificate.h" +#include "net/base/net_errors.h" #include "net/base/x509_certificate.h" // TODO(mattm): Try to make this use only X509Certificate stuff rather than NSS @@ -49,14 +50,18 @@ std::string GetCertNameOrNickname(CERTCertificate* os_cert) { } // namespace -CertificateManagerModel::CertificateManagerModel() { +CertificateManagerModel::CertificateManagerModel(Observer* observer) + : observer_(observer) { } CertificateManagerModel::~CertificateManagerModel() { } void CertificateManagerModel::Refresh() { + VLOG(1) << "refresh started"; cert_db_.ListCerts(&cert_list_); + observer_->CertificatesRefreshed(); + VLOG(1) << "refresh finished"; } void CertificateManagerModel::FilterAndBuildOrgGroupingMap( @@ -109,3 +114,24 @@ string16 CertificateManagerModel::GetColumnText( } return rv; } + +int CertificateManagerModel::ImportFromPKCS12(const std::string& data, + const string16& password) { + int result = cert_db_.ImportFromPKCS12(data, password); + if (result == net::OK) + Refresh(); + return result; +} + +int CertificateManagerModel::ExportToPKCS12(const net::CertificateList& certs, + const string16& password, + std::string* output) const { + return cert_db_.ExportToPKCS12(certs, password, output); +} + +bool CertificateManagerModel::Delete(net::X509Certificate* cert) { + bool result = cert_db_.DeleteCertAndKey(cert); + if (result) + Refresh(); + return result; +} diff --git a/chrome/browser/certificate_manager_model.h b/chrome/browser/certificate_manager_model.h index ce45d3a..2598bdf 100644 --- a/chrome/browser/certificate_manager_model.h +++ b/chrome/browser/certificate_manager_model.h @@ -30,10 +30,19 @@ class CertificateManagerModel { COL_EMAIL_ADDRESS, }; - CertificateManagerModel(); + class Observer { + public: + // Called to notify the view that the certificate list has been refreshed. + // TODO(mattm): do a more granular updating strategy? Maybe retrieve new + // list of certs, diff against past list, and then notify of the changes? + virtual void CertificatesRefreshed() = 0; + }; + + explicit CertificateManagerModel(Observer* observer); ~CertificateManagerModel(); - // Refresh the list of certs. Following this call, the view should call + // Refresh the list of certs. Following this call, the observer + // CertificatesRefreshed method will be called so the view can call // FilterAndBuildOrgGroupingMap as necessary to refresh its tree views. void Refresh(); @@ -44,10 +53,27 @@ class CertificateManagerModel { // Get the data to be displayed in |column| for the given |cert|. string16 GetColumnText(const net::X509Certificate& cert, Column column) const; + // Import certificates from PKCS #12 encoded |data|, using the given + // |password|. Returns a net error code on failure. + int ImportFromPKCS12(const std::string& data, const string16& password); + + // Export certificates as PKCS #12 encoded |output|, using the given + // |password|. Returns number of certs exported. + int ExportToPKCS12(const net::CertificateList& certs, + const string16& password, + std::string* output) const; + + // Delete the cert. Returns true on success. |cert| is still valid when this + // function returns. + bool Delete(net::X509Certificate* cert); + private: net::CertDatabase cert_db_; net::CertificateList cert_list_; + // The observer to notify when certificate list is refreshed. + Observer* observer_; + DISALLOW_COPY_AND_ASSIGN(CertificateManagerModel); }; diff --git a/chrome/browser/dom_ui/dom_ui.cc b/chrome/browser/dom_ui/dom_ui.cc index f257d1e0..a7f5a52 100644 --- a/chrome/browser/dom_ui/dom_ui.cc +++ b/chrome/browser/dom_ui/dom_ui.cc @@ -111,6 +111,12 @@ void DOMUI::CallJavascriptFunction( ExecuteJavascript(GetJavascript(function_name, args)); } +void DOMUI::CallJavascriptFunction( + const std::wstring& function_name, + const std::vector<const Value*>& args) { + ExecuteJavascript(GetJavascript(function_name, args)); +} + ThemeProvider* DOMUI::GetThemeProvider() const { return tab_contents_->profile()->GetThemeProvider(); } diff --git a/chrome/browser/dom_ui/dom_ui.h b/chrome/browser/dom_ui/dom_ui.h index d180a51..e6df31a 100644 --- a/chrome/browser/dom_ui/dom_ui.h +++ b/chrome/browser/dom_ui/dom_ui.h @@ -111,6 +111,8 @@ class DOMUI { const Value& arg2, const Value& arg3, const Value& arg4); + void CallJavascriptFunction(const std::wstring& function_name, + const std::vector<const Value*>& args); ThemeProvider* GetThemeProvider() const; diff --git a/chrome/browser/dom_ui/options/certificate_manager_handler.cc b/chrome/browser/dom_ui/options/certificate_manager_handler.cc index b73e51c..27dc56a 100644 --- a/chrome/browser/dom_ui/options/certificate_manager_handler.cc +++ b/chrome/browser/dom_ui/options/certificate_manager_handler.cc @@ -6,11 +6,15 @@ #include "app/l10n_util.h" #include "app/l10n_util_collator.h" +#include "base/file_util.h" // for FileAccessProvider +#include "base/safe_strerror_posix.h" #include "base/string_number_conversions.h" #include "base/values.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/certificate_manager_model.h" #include "chrome/browser/certificate_viewer.h" +#include "chrome/browser/chrome_thread.h" // for FileAccessProvider +#include "chrome/browser/gtk/certificate_dialogs.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tab_contents/tab_contents_view.h" #include "grit/generated_resources.h" @@ -24,6 +28,12 @@ static const char kNameId[] = "name"; static const char kIconId[] = "icon"; static const char kSecurityDeviceId[] = "device"; +// Enumeration of different callers of SelectFile. +enum { + EXPORT_PERSONAL_FILE_SELECTED, + IMPORT_PERSONAL_FILE_SELECTED, +}; + // TODO(mattm): These are duplicated from cookies_view_handler.cc // Encodes a pointer value into a hex string. std::string PointerToHexString(const void* pointer) { @@ -52,11 +62,26 @@ std::string CertToId(const net::X509Certificate& cert) { net::X509Certificate* IdToCert(const std::string& id) { if (!StartsWithASCII(id, "cert-", true)) return NULL; - return reinterpret_cast<net::X509Certificate*>(HexStringToPointer(id.substr(5))); + return reinterpret_cast<net::X509Certificate*>( + HexStringToPointer(id.substr(5))); +} + +net::X509Certificate* CallbackArgsToCert(const ListValue* args) { + std::string node_id; + if (!args->GetString(0, &node_id)){ + return NULL; + } + net::X509Certificate* cert = IdToCert(node_id); + if (!cert) { + NOTREACHED(); + return NULL; + } + return cert; } struct DictionaryIdComparator { - DictionaryIdComparator(icu::Collator* collator) : collator_(collator) { + explicit DictionaryIdComparator(icu::Collator* collator) + : collator_(collator) { } bool operator()(const Value* a, @@ -80,8 +105,112 @@ struct DictionaryIdComparator { } // namespace -CertificateManagerHandler::CertificateManagerHandler() { - certificate_manager_model_.reset(new CertificateManagerModel); +/////////////////////////////////////////////////////////////////////////////// +// FileAccessProvider + +// TODO(mattm): Move to some shared location? +class FileAccessProvider + : public base::RefCountedThreadSafe<FileAccessProvider>, + public CancelableRequestProvider { + public: + // Reports 0 on success or errno on failure, and the data of the file upon + // success. + // TODO(mattm): don't pass std::string by value.. could use RefCountedBytes + // but it's a vector. Maybe do the derive from CancelableRequest thing + // described in cancelable_request.h? + typedef Callback2<int, std::string>::Type ReadCallback; + + // Reports 0 on success or errno on failure, and the number of bytes written, + // on success. + typedef Callback2<int, int>::Type WriteCallback; + + Handle StartRead(const FilePath& path, + CancelableRequestConsumerBase* consumer, + ReadCallback* callback); + Handle StartWrite(const FilePath& path, + const std::string& data, + CancelableRequestConsumerBase* consumer, + WriteCallback* callback); + + private: + void DoRead(scoped_refptr<CancelableRequest<ReadCallback> > request, + FilePath path); + void DoWrite(scoped_refptr<CancelableRequest<WriteCallback> > request, + FilePath path, + std::string data); +}; + +CancelableRequestProvider::Handle FileAccessProvider::StartRead( + const FilePath& path, + CancelableRequestConsumerBase* consumer, + FileAccessProvider::ReadCallback* callback) { + scoped_refptr<CancelableRequest<ReadCallback> > request( + new CancelableRequest<ReadCallback>(callback)); + AddRequest(request, consumer); + + // Send the parameters and the request to the file thread. + ChromeThread::PostTask( + ChromeThread::FILE, FROM_HERE, + NewRunnableMethod(this, &FileAccessProvider::DoRead, request, path)); + + // The handle will have been set by AddRequest. + return request->handle(); +} + +CancelableRequestProvider::Handle FileAccessProvider::StartWrite( + const FilePath& path, + const std::string& data, + CancelableRequestConsumerBase* consumer, + WriteCallback* callback) { + scoped_refptr<CancelableRequest<WriteCallback> > request( + new CancelableRequest<WriteCallback>(callback)); + AddRequest(request, consumer); + + // Send the parameters and the request to the file thWrite. + ChromeThread::PostTask( + ChromeThread::FILE, FROM_HERE, + NewRunnableMethod( + this, &FileAccessProvider::DoWrite, request, path, data)); + + // The handle will have been set by AddRequest. + return request->handle(); +} + +void FileAccessProvider::DoRead( + scoped_refptr<CancelableRequest<ReadCallback> > request, + FilePath path) { + if (request->canceled()) + return; + + std::string data; + VLOG(1) << "DoRead starting read"; + bool success = file_util::ReadFileToString(path, &data); + int saved_errno = success ? 0 : errno; + VLOG(1) << "DoRead done read: " << success << " " << data.size(); + request->ForwardResult(ReadCallback::TupleType(saved_errno, data)); +} + +void FileAccessProvider::DoWrite( + scoped_refptr<CancelableRequest<WriteCallback> > request, + FilePath path, + std::string data) { + VLOG(1) << "DoWrite starting write"; + int bytes_written = file_util::WriteFile(path, data.data(), data.size()); + int saved_errno = bytes_written >= 0 ? 0 : errno; + VLOG(1) << "DoWrite done write " << bytes_written; + + if (request->canceled()) + return; + + request->ForwardResult(WriteCallback::TupleType(saved_errno, bytes_written)); +} + +/////////////////////////////////////////////////////////////////////////////// +// CertificateManagerHandler + +CertificateManagerHandler::CertificateManagerHandler() + : file_access_provider_(new FileAccessProvider) { + certificate_manager_model_.reset(new CertificateManagerModel(this)); } CertificateManagerHandler::~CertificateManagerHandler() { @@ -135,38 +264,319 @@ void CertificateManagerHandler::GetLocalizedValues( // Buttons. localized_strings->SetString("view_certificate", l10n_util::GetStringUTF16(IDS_CERT_MANAGER_VIEW_CERT_BUTTON)); + localized_strings->SetString("import_certificate", + l10n_util::GetStringUTF16(IDS_CERT_MANAGER_IMPORT_BUTTON)); + localized_strings->SetString("export_certificate", + l10n_util::GetStringUTF16(IDS_CERT_MANAGER_EXPORT_BUTTON)); + localized_strings->SetString("export_all_certificates", + l10n_util::GetStringUTF16(IDS_CERT_MANAGER_EXPORT_ALL_BUTTON)); + localized_strings->SetString("edit_certificate", + l10n_util::GetStringUTF16(IDS_CERT_MANAGER_EDIT_BUTTON)); + localized_strings->SetString("delete_certificate", + l10n_util::GetStringUTF16(IDS_CERT_MANAGER_DELETE_BUTTON)); + + // Certificate Delete overlay strings. + localized_strings->SetString("personalCertsTabDeleteConfirm", + l10n_util::GetStringUTF16(IDS_CERT_MANAGER_DELETE_USER_FORMAT)); + localized_strings->SetString("personalCertsTabDeleteImpact", + l10n_util::GetStringUTF16(IDS_CERT_MANAGER_DELETE_USER_DESCRIPTION)); + // For now, use the "unknown" strings for email certs too. Maybe we should + // just get rid of the email tab. + localized_strings->SetString("emailCertsTabDeleteConfirm", + l10n_util::GetStringUTF16(IDS_CERT_MANAGER_DELETE_UNKNOWN_FORMAT)); + localized_strings->SetString("emailCertsTabDeleteImpact", ""); + localized_strings->SetString("serverCertsTabDeleteConfirm", + l10n_util::GetStringUTF16(IDS_CERT_MANAGER_DELETE_SERVER_FORMAT)); + localized_strings->SetString("serverCertsTabDeleteImpact", + l10n_util::GetStringUTF16(IDS_CERT_MANAGER_DELETE_SERVER_DESCRIPTION)); + localized_strings->SetString("caCertsTabDeleteConfirm", + l10n_util::GetStringUTF16(IDS_CERT_MANAGER_DELETE_CA_FORMAT)); + localized_strings->SetString("caCertsTabDeleteImpact", + l10n_util::GetStringUTF16(IDS_CERT_MANAGER_DELETE_CA_DESCRIPTION)); + localized_strings->SetString("unknownCertsTabDeleteConfirm", + l10n_util::GetStringUTF16(IDS_CERT_MANAGER_DELETE_UNKNOWN_FORMAT)); + localized_strings->SetString("unknownCertsTabDeleteImpact", ""); + + // Certificate Restore overlay strings. + localized_strings->SetString("certificateRestorePasswordDescription", + l10n_util::GetStringUTF16(IDS_CERT_MANAGER_RESTORE_PASSWORD_DESC)); + localized_strings->SetString("certificatePasswordLabel", + l10n_util::GetStringUTF16(IDS_CERT_MANAGER_PASSWORD_LABEL)); + + // Personal Certificate Export overlay strings. + localized_strings->SetString("certificateExportPasswordDescription", + l10n_util::GetStringUTF16(IDS_CERT_MANAGER_EXPORT_PASSWORD_DESC)); + localized_strings->SetString("certificateExportPasswordHelp", + l10n_util::GetStringUTF16(IDS_CERT_MANAGER_EXPORT_PASSWORD_HELP)); + localized_strings->SetString("certificateConfirmPasswordLabel", + l10n_util::GetStringUTF16(IDS_CERT_MANAGER_CONFIRM_PASSWORD_LABEL)); } void CertificateManagerHandler::RegisterMessages() { dom_ui_->RegisterMessageCallback("viewCertificate", NewCallback(this, &CertificateManagerHandler::View)); + dom_ui_->RegisterMessageCallback("editCaCertificate", + NewCallback(this, &CertificateManagerHandler::EditCA)); + + dom_ui_->RegisterMessageCallback("editServerCertificate", + NewCallback(this, &CertificateManagerHandler::EditServer)); + + dom_ui_->RegisterMessageCallback("cancelImportExportCertificate", + NewCallback(this, &CertificateManagerHandler::CancelImportExportProcess)); + + dom_ui_->RegisterMessageCallback("exportPersonalCertificate", + NewCallback(this, &CertificateManagerHandler::ExportPersonal)); + dom_ui_->RegisterMessageCallback("exportAllPersonalCertificates", + NewCallback(this, &CertificateManagerHandler::ExportAllPersonal)); + dom_ui_->RegisterMessageCallback("exportPersonalCertificatePasswordSelected", + NewCallback(this, + &CertificateManagerHandler::ExportPersonalPasswordSelected)); + + dom_ui_->RegisterMessageCallback("importPersonalCertificate", + NewCallback(this, &CertificateManagerHandler::StartImportPersonal)); + dom_ui_->RegisterMessageCallback("importPersonalCertificatePasswordSelected", + NewCallback(this, + &CertificateManagerHandler::ImportPersonalPasswordSelected)); + + dom_ui_->RegisterMessageCallback("importCaCertificate", + NewCallback(this, &CertificateManagerHandler::ImportCA)); + + dom_ui_->RegisterMessageCallback("exportCertificate", + NewCallback(this, &CertificateManagerHandler::Export)); + + dom_ui_->RegisterMessageCallback("deleteCertificate", + NewCallback(this, &CertificateManagerHandler::Delete)); + dom_ui_->RegisterMessageCallback("populateCertificateManager", NewCallback(this, &CertificateManagerHandler::Populate)); } +void CertificateManagerHandler::CertificatesRefreshed() { + PopulateTree("personalCertsTab", net::USER_CERT); + PopulateTree("emailCertsTab", net::EMAIL_CERT); + PopulateTree("serverCertsTab", net::SERVER_CERT); + PopulateTree("caCertsTab", net::CA_CERT); + PopulateTree("otherCertsTab", net::UNKNOWN_CERT); + VLOG(1) << "populating finished"; +} + +void CertificateManagerHandler::FileSelected(const FilePath& path, int index, + void* params) { + switch (reinterpret_cast<intptr_t>(params)) { + case EXPORT_PERSONAL_FILE_SELECTED: + ExportPersonalFileSelected(path); + break; + case IMPORT_PERSONAL_FILE_SELECTED: + ImportPersonalFileSelected(path); + break; + default: + NOTREACHED(); + } +} + +void CertificateManagerHandler::FileSelectionCanceled(void* params) { + switch (reinterpret_cast<intptr_t>(params)) { + case EXPORT_PERSONAL_FILE_SELECTED: + case IMPORT_PERSONAL_FILE_SELECTED: + ImportExportCleanup(); + break; + default: + NOTREACHED(); + } +} + void CertificateManagerHandler::View(const ListValue* args) { - std::string node_id; - if (!args->GetString(0, &node_id)){ + net::X509Certificate* cert = CallbackArgsToCert(args); + if (!cert) + return; + ShowCertificateViewer(GetParentWindow(), cert); +} + +void CertificateManagerHandler::EditCA(const ListValue* args) { + NOTIMPLEMENTED(); +} + +void CertificateManagerHandler::EditServer(const ListValue* args) { + NOTIMPLEMENTED(); +} + +void CertificateManagerHandler::ExportPersonal(const ListValue* args) { + net::X509Certificate* cert = CallbackArgsToCert(args); + if (!cert) + return; + + selected_cert_list_.push_back(cert); + + SelectFileDialog::FileTypeInfo file_type_info; + file_type_info.extensions.resize(1); + file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("p12")); + file_type_info.extension_description_overrides.push_back( + l10n_util::GetStringUTF16(IDS_CERT_MANAGER_PKCS12_FILES)); + file_type_info.include_all_files = true; + select_file_dialog_ = SelectFileDialog::Create(this); + select_file_dialog_->SelectFile( + SelectFileDialog::SELECT_SAVEAS_FILE, string16(), + FilePath(), &file_type_info, 1, + FILE_PATH_LITERAL("p12"), GetParentWindow(), + reinterpret_cast<void*>(EXPORT_PERSONAL_FILE_SELECTED)); +} + +void CertificateManagerHandler::ExportAllPersonal(const ListValue* args) { + NOTIMPLEMENTED(); +} + +void CertificateManagerHandler::ExportPersonalFileSelected( + const FilePath& path) { + file_path_ = path; + dom_ui_->CallJavascriptFunction( + L"CertificateManager.exportPersonalAskPassword"); +} + +void CertificateManagerHandler::ExportPersonalPasswordSelected( + const ListValue* args) { + if (!args->GetString(0, &password_)){ + dom_ui_->CallJavascriptFunction(L"CertificateRestoreOverlay.dismiss"); + ImportExportCleanup(); return; } - net::X509Certificate* cert = IdToCert(node_id); - if (!cert) { - NOTREACHED(); + std::string output; + int num_exported = certificate_manager_model_->ExportToPKCS12( + selected_cert_list_, + password_, + &output); + if (!num_exported) { + ShowError( + l10n_util::GetStringUTF8(IDS_CERT_MANAGER_PKCS12_EXPORT_ERROR_TITLE), + l10n_util::GetStringUTF8(IDS_CERT_MANAGER_UNKNOWN_ERROR)); + dom_ui_->CallJavascriptFunction(L"CertificateRestoreOverlay.dismiss"); + ImportExportCleanup(); + return; + } + file_access_provider_->StartWrite( + file_path_, + output, + &consumer_, + NewCallback(this, &CertificateManagerHandler::ExportPersonalFileWritten)); +} + +void CertificateManagerHandler::ExportPersonalFileWritten(int write_errno, + int bytes_written) { + dom_ui_->CallJavascriptFunction(L"CertificateRestoreOverlay.dismiss"); + ImportExportCleanup(); + if (write_errno) { + ShowError( + l10n_util::GetStringUTF8(IDS_CERT_MANAGER_PKCS12_EXPORT_ERROR_TITLE), + l10n_util::GetStringFUTF8(IDS_CERT_MANAGER_WRITE_ERROR_FORMAT, + UTF8ToUTF16(safe_strerror(write_errno)))); + } +} + +void CertificateManagerHandler::StartImportPersonal(const ListValue* args) { + SelectFileDialog::FileTypeInfo file_type_info; + file_type_info.extensions.resize(1); + file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("p12")); + file_type_info.extension_description_overrides.push_back( + l10n_util::GetStringUTF16(IDS_CERT_MANAGER_PKCS12_FILES)); + file_type_info.include_all_files = true; + select_file_dialog_ = SelectFileDialog::Create(this); + select_file_dialog_->SelectFile( + SelectFileDialog::SELECT_OPEN_FILE, string16(), + FilePath(), &file_type_info, 1, + FILE_PATH_LITERAL("p12"), GetParentWindow(), + reinterpret_cast<void*>(IMPORT_PERSONAL_FILE_SELECTED)); +} + +void CertificateManagerHandler::ImportPersonalFileSelected( + const FilePath& path) { + file_path_ = path; + dom_ui_->CallJavascriptFunction( + L"CertificateManager.importPersonalAskPassword"); +} + +void CertificateManagerHandler::ImportPersonalPasswordSelected( + const ListValue* args) { + if (!args->GetString(0, &password_)){ + dom_ui_->CallJavascriptFunction(L"CertificateRestoreOverlay.dismiss"); + ImportExportCleanup(); + return; + } + file_access_provider_->StartRead( + file_path_, + &consumer_, + NewCallback(this, &CertificateManagerHandler::ImportPersonalFileRead)); +} + +void CertificateManagerHandler::ImportPersonalFileRead( + int read_errno, std::string data) { + if (read_errno) { + ImportExportCleanup(); + dom_ui_->CallJavascriptFunction(L"CertificateRestoreOverlay.dismiss"); + ShowError( + l10n_util::GetStringUTF8(IDS_CERT_MANAGER_PKCS12_IMPORT_ERROR_TITLE), + l10n_util::GetStringFUTF8(IDS_CERT_MANAGER_READ_ERROR_FORMAT, + UTF8ToUTF16(safe_strerror(read_errno)))); return; } - ShowCertificateViewer( - dom_ui_->tab_contents()->view()->GetTopLevelNativeWindow(), cert); + int result = certificate_manager_model_->ImportFromPKCS12(data, password_); + ImportExportCleanup(); + dom_ui_->CallJavascriptFunction(L"CertificateRestoreOverlay.dismiss"); + switch (result) { + case net::OK: + break; + case net::ERR_PKCS12_IMPORT_BAD_PASSWORD: + ShowError( + l10n_util::GetStringUTF8(IDS_CERT_MANAGER_PKCS12_IMPORT_ERROR_TITLE), + l10n_util::GetStringUTF8(IDS_CERT_MANAGER_BAD_PASSWORD)); + // TODO(mattm): if the error was a bad password, we should reshow the + // password dialog after the user dismisses the error dialog. + break; + default: + ShowError( + l10n_util::GetStringUTF8(IDS_CERT_MANAGER_PKCS12_IMPORT_ERROR_TITLE), + l10n_util::GetStringUTF8(IDS_CERT_MANAGER_UNKNOWN_ERROR)); + break; + } +} + +void CertificateManagerHandler::CancelImportExportProcess( + const ListValue* args) { + ImportExportCleanup(); +} + +void CertificateManagerHandler::ImportExportCleanup() { + file_path_.clear(); + password_.clear(); + selected_cert_list_.clear(); + select_file_dialog_ = NULL; +} + +void CertificateManagerHandler::ImportCA(const ListValue* args) { + NOTIMPLEMENTED(); +} + +void CertificateManagerHandler::Export(const ListValue* args) { + net::X509Certificate* cert = CallbackArgsToCert(args); + if (!cert) + return; + ShowCertExportDialog(GetParentWindow(), cert->os_cert_handle()); +} + +void CertificateManagerHandler::Delete(const ListValue* args) { + net::X509Certificate* cert = CallbackArgsToCert(args); + if (!cert) + return; + bool result = certificate_manager_model_->Delete(cert); + if (!result) { + // TODO(mattm): better error messages? + ShowError( + l10n_util::GetStringUTF8(IDS_CERT_MANAGER_DELETE_CERT_ERROR_TITLE), + l10n_util::GetStringUTF8(IDS_CERT_MANAGER_UNKNOWN_ERROR)); + } } void CertificateManagerHandler::Populate(const ListValue* args) { certificate_manager_model_->Refresh(); - - PopulateTree("personalCertsTab", net::USER_CERT); - PopulateTree("emailCertsTab", net::EMAIL_CERT); - PopulateTree("serverCertsTab", net::SERVER_CERT); - PopulateTree("caCertsTab", net::CA_CERT); - PopulateTree("otherCertsTab", net::UNKNOWN_CERT); } void CertificateManagerHandler::PopulateTree(const std::string& tab_name, @@ -223,3 +633,19 @@ void CertificateManagerHandler::PopulateTree(const std::string& tab_name, dom_ui_->CallJavascriptFunction(L"CertificateManager.onPopulateTree", args); } } + +void CertificateManagerHandler::ShowError(const std::string& title, + const std::string& error) const { + std::vector<const Value*> args; + args.push_back(Value::CreateStringValue(title)); + args.push_back(Value::CreateStringValue(error)); + args.push_back(Value::CreateNullValue()); // okTitle + args.push_back(Value::CreateStringValue("")); // cancelTitle + args.push_back(Value::CreateNullValue()); // okCallback + args.push_back(Value::CreateNullValue()); // cancelCallback + dom_ui_->CallJavascriptFunction(L"AlertOverlay.show", args); +} + +gfx::NativeWindow CertificateManagerHandler::GetParentWindow() const { + return dom_ui_->tab_contents()->view()->GetTopLevelNativeWindow(); +} diff --git a/chrome/browser/dom_ui/options/certificate_manager_handler.h b/chrome/browser/dom_ui/options/certificate_manager_handler.h index 3766b57..48534f5 100644 --- a/chrome/browser/dom_ui/options/certificate_manager_handler.h +++ b/chrome/browser/dom_ui/options/certificate_manager_handler.h @@ -7,12 +7,18 @@ #pragma once #include "base/scoped_ptr.h" +#include "chrome/browser/cancelable_request.h" +#include "chrome/browser/certificate_manager_model.h" #include "chrome/browser/dom_ui/options/options_ui.h" +#include "chrome/browser/shell_dialogs.h" +#include "gfx/native_widget_types.h" #include "net/base/cert_database.h" -class CertificateManagerModel; +class FileAccessProvider; -class CertificateManagerHandler : public OptionsPageUIHandler { +class CertificateManagerHandler : public OptionsPageUIHandler, + public CertificateManagerModel::Observer, + public SelectFileDialog::Listener { public: CertificateManagerHandler(); virtual ~CertificateManagerHandler(); @@ -21,19 +27,94 @@ class CertificateManagerHandler : public OptionsPageUIHandler { virtual void GetLocalizedValues(DictionaryValue* localized_strings); virtual void RegisterMessages(); + // CertificateManagerModel::Observer implementation. + virtual void CertificatesRefreshed(); + + // SelectFileDialog::Listener implementation. + virtual void FileSelected(const FilePath& path, int index, void* params); + virtual void FileSelectionCanceled(void* params); + private: // View certificate. void View(const ListValue* args); + // Edit server certificate trust values. + void EditServer(const ListValue* args); + + // Edit certificate authority trust values. + void EditCA(const ListValue* args); + + // Cleanup state stored during import or export process. + void CancelImportExportProcess(const ListValue* args); + void ImportExportCleanup(); + + // Export to PKCS #12 file. The sequence goes like: + // 1a. user click on export button -> ExportPersonal -> launches file + // selector + // 1b. user click on export all button -> ExportAllPersonal -> launches file + // selector + // 2. user selects file -> ExportPersonalFileSelected -> launches password + // dialog + // 3. user enters password -> ExportPersonalPasswordSelected -> exports to + // memory buffer -> starts async write operation + // 4. write finishes (or fails) -> ExportPersonalFileWritten + void ExportPersonal(const ListValue* args); + void ExportAllPersonal(const ListValue* args); + void ExportPersonalFileSelected(const FilePath& path); + void ExportPersonalPasswordSelected(const ListValue* args); + void ExportPersonalFileWritten(int write_errno, int bytes_written); + + // Import from PKCS #12 file. The sequence goes like: + // 1. user click on import button -> StartImportPersonal -> launches file + // selector + // 2. user selects file -> ImportPersonalFileSelected -> launches password + // dialog + // 3. user enters password -> ImportPersonalPasswordSelected -> starts async + // read operation + // 4. read operation completes -> ImportPersonalFileRead -> attempts to + // import with previously entered password + // 5a. if import succeeds -> ImportExportCleanup + // 5b. if import fails -> show error, ImportExportCleanup + // TODO(mattm): allow retrying with different password + void StartImportPersonal(const ListValue* args); + void ImportPersonalFileSelected(const FilePath& path); + void ImportPersonalPasswordSelected(const ListValue* args); + void ImportPersonalFileRead(int read_errno, std::string data); + + void ImportCA(const ListValue* args); + + // Export a certificate. + void Export(const ListValue* args); + + // Delete certificate and private key (if any). + void Delete(const ListValue* args); + // Populate the trees in all the tabs. void Populate(const ListValue* args); // Populate the given tab's tree. void PopulateTree(const std::string& tab_name, net::CertType type); + // Display a domui error message box. + void ShowError(const std::string& title, const std::string& error) const; + + gfx::NativeWindow GetParentWindow() const; + // The Certificates Manager model scoped_ptr<CertificateManagerModel> certificate_manager_model_; + // For multi-step import or export processes, we need to store the path, + // password, etc the user chose while we wait for them to enter a password, + // wait for file to be read, etc. + FilePath file_path_; + string16 password_; + net::CertificateList selected_cert_list_; + scoped_refptr<SelectFileDialog> select_file_dialog_; + + // Used in reading and writing certificate files. + CancelableRequestConsumer consumer_; + scoped_refptr<FileAccessProvider> file_access_provider_; + DISALLOW_COPY_AND_ASSIGN(CertificateManagerHandler); }; diff --git a/chrome/browser/gtk/certificate_dialogs.cc b/chrome/browser/gtk/certificate_dialogs.cc index ba12f6d..a42a1ad 100644 --- a/chrome/browser/gtk/certificate_dialogs.cc +++ b/chrome/browser/gtk/certificate_dialogs.cc @@ -2,10 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(mattm): this isn't gtk specific, it shouldn't be under the gtk dir + +// TODO(mattm): the nss export functions should be moved to cert_database_nss or +// x509_certificate_nss. + #include "chrome/browser/gtk/certificate_dialogs.h" #include <cms.h> -#include <gtk/gtk.h> #include <vector> diff --git a/chrome/browser/resources/options.html b/chrome/browser/resources/options.html index 686a17c..15aac2c 100644 --- a/chrome/browser/resources/options.html +++ b/chrome/browser/resources/options.html @@ -82,8 +82,12 @@ <if expr="not pp_ifdef('win32') and not pp_ifdef('darwin')"> <script src="options/certificate_tree.js"></script> <script src="options/certificate_manager.js"></script> + <script src="options/certificate_restore_overlay.js"></script> + <script src="options/certificate_backup_overlay.js"></script> <script> var CertificateManager = options.CertificateManager; + var CertificateRestoreOverlay = options.CertificateRestoreOverlay; + var CertificateBackupOverlay = options.CertificateBackupOverlay; </script> </if> <script src="options/add_startup_page_overlay.js"></script> @@ -163,6 +167,8 @@ function load() { OptionsPage.registerSubPage(SyncOptions.getInstance()); if (!cr.isWindows && !cr.isMac) { OptionsPage.registerSubPage(CertificateManager.getInstance()); + OptionsPage.registerOverlay(CertificateRestoreOverlay.getInstance()); + OptionsPage.registerOverlay(CertificateBackupOverlay.getInstance()); } OptionsPage.registerOverlay(AddStartupPageOverlay.getInstance()); OptionsPage.registerOverlay(AlertOverlay.getInstance()); @@ -269,6 +275,10 @@ window.onpopstate = function(e) { src="options/chromeos_language_customize_modifier_keys_overlay.html"> <include src="options/chromeos_internet_detail.html"> </if> + <if expr="not pp_ifdef('win32') and not pp_ifdef('darwin')"> + <include src="options/certificate_restore_overlay.html"> + <include src="options/certificate_backup_overlay.html"> + </if> </div> <div id="main-content"> <div id="navbar-container"> diff --git a/chrome/browser/resources/options/certificate_backup_overlay.html b/chrome/browser/resources/options/certificate_backup_overlay.html new file mode 100644 index 0000000..0565e05 --- /dev/null +++ b/chrome/browser/resources/options/certificate_backup_overlay.html @@ -0,0 +1,42 @@ +<div class="page hidden" id="certificateBackupOverlay"> + <p> + <span i18n-content="certificateExportPasswordDescription"></span> + </p> + + <p> + <table> + <tr> + <label id="certificateBackupPasswordLabel"> + <td> + <span i18n-content="certificatePasswordLabel"></span> + </td> + <td> + <input type="password" id="certificateBackupPassword"> + </td> + </label> + </tr> + <tr> + <label id="certificateBackupPasswordLabel"> + <td> + <span i18n-content="certificateConfirmPasswordLabel"></span> + </td> + <td> + <input type="password" id="certificateBackupPassword2"> + </td> + </label> + </tr> + </table> + </p> + + <p> + <span i18n-content="certificateExportPasswordHelp"></span> + </p> + + <div class="button-strip"> + <button type="submit" id="certificateBackupOkButton" + i18n-content="ok" disabled></button> + <button type="reset" id="certificateBackupCancelButton" + i18n-content="cancel"></button> + </div> +</div> + diff --git a/chrome/browser/resources/options/certificate_backup_overlay.js b/chrome/browser/resources/options/certificate_backup_overlay.js new file mode 100644 index 0000000..f5da035 --- /dev/null +++ b/chrome/browser/resources/options/certificate_backup_overlay.js @@ -0,0 +1,118 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + const OptionsPage = options.OptionsPage; + + /** + * CertificateBackupOverlay class + * Encapsulated handling of the 'enter backup password' overlay page. + * @class + */ + function CertificateBackupOverlay() { + OptionsPage.call(this, 'certificateBackupOverlay', + '', + 'certificateBackupOverlay'); + } + + cr.addSingletonGetter(CertificateBackupOverlay); + + CertificateBackupOverlay.prototype = { + __proto__: OptionsPage.prototype, + + /** + * Initializes the page. + */ + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + + var self = this; + $('certificateBackupCancelButton').onclick = function(event) { + self.cancelBackup_(); + } + $('certificateBackupOkButton').onclick = function(event) { + self.finishBackup_(); + } + $('certificateBackupPassword').oninput = + $('certificateBackupPassword2').oninput = function(event) { + self.comparePasswords_(); + } + + self.clearInputFields_(); + }, + + /** + * Clears any uncommitted input, and dismisses the overlay. + * @private + */ + dismissOverlay_: function() { + this.clearInputFields_(); + OptionsPage.clearOverlays(); + }, + + /** + * Attempt the Backup operation. + * The overlay will be left up with inputs disabled until the backend + * finishes and dismisses it. + * @private + */ + finishBackup_: function() { + chrome.send('exportPersonalCertificatePasswordSelected', + [$('certificateBackupPassword').value]); + $('certificateBackupCancelButton').disabled = true; + $('certificateBackupOkButton').disabled = true; + $('certificateBackupPassword').disabled = true; + $('certificateBackupPassword2').disabled = true; + }, + + /** + * Cancel the Backup operation. + * @private + */ + cancelBackup_: function() { + chrome.send('cancelImportExportCertificate'); + this.dismissOverlay_(); + }, + + /** + * Compares the password fields and sets the button state appropriately. + * @private + */ + comparePasswords_: function() { + var password1 = $('certificateBackupPassword').value; + var password2 = $('certificateBackupPassword2').value; + $('certificateBackupOkButton').disabled = + !password1 || password1 != password2; + }, + + /** + * Clears the value of each input field. + * @private + */ + clearInputFields_: function() { + $('certificateBackupPassword').value = ''; + $('certificateBackupPassword2').value = ''; + $('certificateBackupPassword').disabled = false; + $('certificateBackupPassword2').disabled = false; + $('certificateBackupCancelButton').disabled = false; + $('certificateBackupOkButton').disabled = true; + }, + }; + + CertificateBackupOverlay.show = function() { + CertificateBackupOverlay.getInstance().clearInputFields_(); + OptionsPage.showOverlay('certificateBackupOverlay'); + }; + + CertificateBackupOverlay.dismiss = function() { + CertificateBackupOverlay.getInstance().dismissOverlay_(); + }; + + // Export + return { + CertificateBackupOverlay: CertificateBackupOverlay + }; + +}); + diff --git a/chrome/browser/resources/options/certificate_manager.html b/chrome/browser/resources/options/certificate_manager.html index 32821f2..14b0feb 100644 --- a/chrome/browser/resources/options/certificate_manager.html +++ b/chrome/browser/resources/options/certificate_manager.html @@ -33,7 +33,17 @@ <tr><td> <button id="personalCertsTab-view" i18n-content="view_certificate" disabled></button> - <!-- TODO other buttons --> + <button id="personalCertsTab-backup" i18n-content="export_certificate" + disabled></button> + <!-- TODO(mattm): + <button id="personalCertsTab-backup-all" + i18n-content="export_all_certificates" + disabled></button> + --> + <button id="personalCertsTab-import" i18n-content="import_certificate" + ></button> + <button id="personalCertsTab-delete" i18n-content="delete_certificate" + disabled></button> </td></tr> </table> </div> @@ -49,7 +59,17 @@ <tr><td> <button id="emailCertsTab-view" i18n-content="view_certificate" disabled></button> - <!-- TODO other buttons --> + <!-- We don't really care about email certificates, so not much point + bothering to implement these... + <button id="emailCertsTab-edit" i18n-content="edit_certificate" + disabled></button> + <button id="emailCertsTab-import" i18n-content="import_certificate" + ></button> + --> + <button id="emailCertsTab-export" i18n-content="export_certificate" + disabled></button> + <button id="emailCertsTab-delete" i18n-content="delete_certificate" + disabled></button> </td></tr> </table> </div> @@ -65,7 +85,16 @@ <tr><td> <button id="serverCertsTab-view" i18n-content="view_certificate" disabled></button> - <!-- TODO other buttons --> + <!-- TODO(mattm): + <button id="serverCertsTab-edit" i18n-content="edit_certificate" + disabled></button> + <button id="serverCertsTab-import" i18n-content="import_certificate" + ></button> + --> + <button id="serverCertsTab-export" i18n-content="export_certificate" + disabled></button> + <button id="serverCertsTab-delete" i18n-content="delete_certificate" + disabled></button> </td></tr> </table> </div> @@ -81,7 +110,16 @@ <tr><td> <button id="caCertsTab-view" i18n-content="view_certificate" disabled></button> - <!-- TODO other buttons --> + <!-- TODO(mattm): + <button id="caCertsTab-edit" i18n-content="edit_certificate" + disabled></button> + <button id="caCertsTab-import" i18n-content="import_certificate" + ></button> + --> + <button id="caCertsTab-export" i18n-content="export_certificate" + disabled></button> + <button id="caCertsTab-delete" i18n-content="delete_certificate" + disabled></button> </td></tr> </table> </div> @@ -97,7 +135,10 @@ <tr><td> <button id="otherCertsTab-view" i18n-content="view_certificate" disabled></button> - <!-- TODO other buttons --> + <button id="otherCertsTab-export" i18n-content="export_certificate" + disabled></button> + <button id="otherCertsTab-delete" i18n-content="delete_certificate" + disabled></button> </td></tr> </table> </div> diff --git a/chrome/browser/resources/options/certificate_manager.js b/chrome/browser/resources/options/certificate_manager.js index 741809e..9bcba83 100644 --- a/chrome/browser/resources/options/certificate_manager.js +++ b/chrome/browser/resources/options/certificate_manager.js @@ -20,14 +20,80 @@ cr.define('options', function() { this.tree.addEventListener('change', this.handleCertificatesTreeChange_.bind(this)); + var tree = this.tree; this.viewButton = $(id + '-view'); - - var tree = this.tree; this.viewButton.onclick = function(e) { var selected = tree.selectedItem; chrome.send('viewCertificate', [selected.data.id]); } + + this.editButton = $(id + '-edit'); + if (this.editButton !== null) { + if (id == 'serverCertsTab') { + this.editButton.onclick = function(e) { + var selected = tree.selectedItem; + chrome.send('editServerCertificate', [selected.data.id]); + } + } else if (id == 'caCertsTab') { + this.editButton.onclick = function(e) { + var selected = tree.selectedItem; + chrome.send('editCaCertificate', [selected.data.id]); + } + } else { + console.log('unknown edit button for: ' + id); + } + } + + this.backupButton = $(id + '-backup'); + if (this.backupButton !== null) { + this.backupButton.onclick = function(e) { + var selected = tree.selectedItem; + chrome.send('exportPersonalCertificate', [selected.data.id]); + } + } + + this.backupAllButton = $(id + '-backup-all'); + if (this.backupAllButton !== null) { + this.backupAllButton.onclick = function(e) { + chrome.send('exportAllPersonalCertificates', []); + } + } + + this.importButton = $(id + '-import'); + if (this.importButton !== null) { + if (id == 'personalCertsTab') { + this.importButton.onclick = function(e) { + chrome.send('importPersonalCertificate', []); + } + } else if (id == 'caCertsTab') { + this.importButton.onclick = function(e) { + chrome.send('importCaCertificate', []); + } + } else { + console.log('unknown import button for: ' + id); + } + } + + this.exportButton = $(id + '-export'); + if (this.exportButton !== null) { + this.exportButton.onclick = function(e) { + var selected = tree.selectedItem; + chrome.send('exportCertificate', [selected.data.id]); + } + } + + this.deleteButton = $(id + '-delete'); + this.deleteButton.onclick = function(e) { + var data = tree.selectedItem.data; + AlertOverlay.show( + localStrings.getStringF(id + 'DeleteConfirm', data.name), + localStrings.getString(id + 'DeleteImpact'), + undefined, + undefined, + function() { chrome.send('deleteCertificate', [data.id]); } + ); + } } CertificateManagerTab.prototype = { @@ -39,7 +105,17 @@ cr.define('options', function() { */ updateButtonState: function(data) { var isCert = !!data && data.id.substr(0, 5) == 'cert-'; + var hasChildren = this.tree.items.length > 0; this.viewButton.disabled = !isCert; + if (this.editButton !== null) + this.editButton.disabled = !isCert; + if (this.backupButton !== null) + this.backupButton.disabled = !isCert; + if (this.backupAllButton !== null) + this.backupAllButton.disabled = !hasChildren; + if (this.exportButton !== null) + this.exportButton.disabled = !isCert; + this.deleteButton.disabled = !isCert; }, /** @@ -108,6 +184,14 @@ cr.define('options', function() { $(args[0]).populate(args[1]); }; + CertificateManager.exportPersonalAskPassword = function(args) { + CertificateBackupOverlay.show(); + }; + + CertificateManager.importPersonalAskPassword = function(args) { + CertificateRestoreOverlay.show(); + }; + // Export return { CertificateManagerTab: CertificateManagerTab, diff --git a/chrome/browser/resources/options/certificate_restore_overlay.html b/chrome/browser/resources/options/certificate_restore_overlay.html new file mode 100644 index 0000000..7985ff2 --- /dev/null +++ b/chrome/browser/resources/options/certificate_restore_overlay.html @@ -0,0 +1,19 @@ +<div class="page hidden" id="certificateRestoreOverlay"> + <p> + <span i18n-content="certificateRestorePasswordDescription"></span> + </p> + + <p> + <label id="certificateRestorePasswordLabel"> + <span i18n-content="certificatePasswordLabel"></span> + <input type="password" id="certificateRestorePassword"> + </label> + </p> + + <div class="button-strip"> + <button type="submit" id="certificateRestoreOkButton" + i18n-content="ok"></button> + <button type="reset" id="certificateRestoreCancelButton" + i18n-content="cancel"></button> + </div> +</div> diff --git a/chrome/browser/resources/options/certificate_restore_overlay.js b/chrome/browser/resources/options/certificate_restore_overlay.js new file mode 100644 index 0000000..2673c08 --- /dev/null +++ b/chrome/browser/resources/options/certificate_restore_overlay.js @@ -0,0 +1,97 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + const OptionsPage = options.OptionsPage; + + /** + * CertificateRestoreOverlay class + * Encapsulated handling of the 'enter restore password' overlay page. + * @class + */ + function CertificateRestoreOverlay() { + OptionsPage.call(this, 'certificateRestoreOverlay', + '', + 'certificateRestoreOverlay'); + } + + cr.addSingletonGetter(CertificateRestoreOverlay); + + CertificateRestoreOverlay.prototype = { + __proto__: OptionsPage.prototype, + + /** + * Initializes the page. + */ + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + + var self = this; + $('certificateRestoreCancelButton').onclick = function(event) { + self.cancelRestore_(); + } + $('certificateRestoreOkButton').onclick = function(event) { + self.finishRestore_(); + } + + self.clearInputFields_(); + }, + + /** + * Clears any uncommitted input, and dismisses the overlay. + * @private + */ + dismissOverlay_: function() { + this.clearInputFields_(); + OptionsPage.clearOverlays(); + }, + + /** + * Attempt the restore operation. + * The overlay will be left up with inputs disabled until the backend + * finishes and dismisses it. + * @private + */ + finishRestore_: function() { + chrome.send('importPersonalCertificatePasswordSelected', + [$('certificateRestorePassword').value]); + $('certificateRestoreCancelButton').disabled = true; + $('certificateRestoreOkButton').disabled = true; + }, + + /** + * Cancel the restore operation. + * @private + */ + cancelRestore_: function() { + chrome.send('cancelImportExportCertificate'); + this.dismissOverlay_(); + }, + + /** + * Clears the value of each input field. + * @private + */ + clearInputFields_: function() { + $('certificateRestorePassword').value = ''; + $('certificateRestoreCancelButton').disabled = false; + $('certificateRestoreOkButton').disabled = false; + }, + }; + + CertificateRestoreOverlay.show = function() { + CertificateRestoreOverlay.getInstance().clearInputFields_(); + OptionsPage.showOverlay('certificateRestoreOverlay'); + }; + + CertificateRestoreOverlay.dismiss = function() { + CertificateRestoreOverlay.getInstance().dismissOverlay_(); + }; + + // Export + return { + CertificateRestoreOverlay: CertificateRestoreOverlay + }; + +}); |