Cert Management UI V2: Enable delete functionality for added-by-you certs

Bug: 40928765
Change-Id: I9d927e95fae1bc26d32daa0693df4271f2fb5155
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5925341
Reviewed-by: Matt Mueller <[email protected]>
Commit-Queue: Carlos IL <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1373464}
diff --git a/chrome/app/certificate_manager.grdp b/chrome/app/certificate_manager.grdp
index 6b871db..6c949860 100644
--- a/chrome/app/certificate_manager.grdp
+++ b/chrome/app/certificate_manager.grdp
@@ -159,6 +159,9 @@
         If you delete one of your own certificates, you can no longer use it to identify yourself.
       </message>
     </if>
+    <message name="IDS_SETTINGS_CERTIFICATE_MANAGER_V2_DELETE_SERVER_CERT_DESCRIPTION" desc="Contents of certificate manager dialog confirming deletion of a server certificate.">
+        If you delete a server certificate, Chrome will no longer use it to identify servers.
+    </message>
     <message name="IDS_SETTINGS_CERTIFICATE_MANAGER_V2_READ_FILE_ERROR" desc="Eror message displayed in certificate manager when the user tried to import a certificate but the file could not be read.">
       Error reading file
     </message>
diff --git a/chrome/app/certificate_manager_grdp/IDS_SETTINGS_CERTIFICATE_MANAGER_V2_DELETE_SERVER_CERT_DESCRIPTION.png.sha1 b/chrome/app/certificate_manager_grdp/IDS_SETTINGS_CERTIFICATE_MANAGER_V2_DELETE_SERVER_CERT_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..f462371
--- /dev/null
+++ b/chrome/app/certificate_manager_grdp/IDS_SETTINGS_CERTIFICATE_MANAGER_V2_DELETE_SERVER_CERT_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+25ebb7f9f9ee09cf5f7e31676b55bde9953ef3f6
\ No newline at end of file
diff --git a/chrome/browser/net/server_certificate_database.cc b/chrome/browser/net/server_certificate_database.cc
index 655c3007..dc1e634 100644
--- a/chrome/browser/net/server_certificate_database.cc
+++ b/chrome/browser/net/server_certificate_database.cc
@@ -158,6 +158,16 @@
   return statement.ColumnInt(0);
 }
 
+bool ServerCertificateDatabase::DeleteCertificate(
+    const std::string& sha256hash_hex) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  sql::Statement delete_statement(db_.GetCachedStatement(
+      SQL_FROM_HERE, "DELETE FROM certificates WHERE sha256hash_hex=?"));
+  DCHECK(delete_statement.is_valid());
+  delete_statement.BindString(0, sha256hash_hex);
+  return delete_statement.Run();
+}
+
 ServerCertificateDatabase::CertInformation::CertInformation() = default;
 ServerCertificateDatabase::CertInformation::~CertInformation() = default;
 ServerCertificateDatabase::CertInformation::CertInformation(
diff --git a/chrome/browser/net/server_certificate_database.h b/chrome/browser/net/server_certificate_database.h
index 696ef01..6262adde 100644
--- a/chrome/browser/net/server_certificate_database.h
+++ b/chrome/browser/net/server_certificate_database.h
@@ -56,6 +56,9 @@
 
   uint32_t RetrieveCertificatesCount();
 
+  // Delete the certificate with a matching hash from the database.
+  bool DeleteCertificate(const std::string& sha256hash_hex);
+
  private:
   sql::InitStatus InitInternal(const base::FilePath& storage_dir);
 
diff --git a/chrome/browser/net/server_certificate_database_service.cc b/chrome/browser/net/server_certificate_database_service.cc
index bb1b221..85475de3 100644
--- a/chrome/browser/net/server_certificate_database_service.cc
+++ b/chrome/browser/net/server_certificate_database_service.cc
@@ -139,4 +139,13 @@
       .Then(std::move(callback));
 }
 
+void ServerCertificateDatabaseService::DeleteCertificate(
+    const std::string& sha256hash_hex,
+    base::OnceCallback<void(bool)> callback) {
+  server_cert_database_
+      .AsyncCall(&net::ServerCertificateDatabase::DeleteCertificate)
+      .WithArgs(sha256hash_hex)
+      .Then(std::move(callback));
+}
+
 }  // namespace net
diff --git a/chrome/browser/net/server_certificate_database_service.h b/chrome/browser/net/server_certificate_database_service.h
index 0ad8145..16d88f0 100644
--- a/chrome/browser/net/server_certificate_database_service.h
+++ b/chrome/browser/net/server_certificate_database_service.h
@@ -86,6 +86,9 @@
 
   void GetCertificatesCount(base::OnceCallback<void(uint32_t)> callback);
 
+  void DeleteCertificate(const std::string& sha256hash_hex,
+                         base::OnceCallback<void(bool)> callback);
+
  private:
 #if BUILDFLAG(IS_CHROMEOS)
   void NSSMigrationComplete(
diff --git a/chrome/browser/net/server_certificate_database_unittest.cc b/chrome/browser/net/server_certificate_database_unittest.cc
index a2e8a83..69fb827 100644
--- a/chrome/browser/net/server_certificate_database_unittest.cc
+++ b/chrome/browser/net/server_certificate_database_unittest.cc
@@ -103,6 +103,32 @@
                            CertInfoEquals(std::ref(intermediate_cert_info))));
 }
 
+TEST_F(ServerCertificateDatabaseTest, Delete) {
+  EXPECT_TRUE(database_->RetrieveAllCertificates().empty());
+
+  auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
+
+  ServerCertificateDatabase::CertInformation root_cert_info = MakeCertInfo(
+      root->GetDER(), CertificateTrust::CERTIFICATE_TRUST_TYPE_TRUSTED);
+  ServerCertificateDatabase::CertInformation intermediate_cert_info =
+      MakeCertInfo(intermediate->GetDER(),
+                   CertificateTrust::CERTIFICATE_TRUST_TYPE_UNSPECIFIED);
+
+  EXPECT_TRUE(database_->InsertOrUpdateCert(root_cert_info));
+  EXPECT_TRUE(database_->InsertOrUpdateCert(intermediate_cert_info));
+
+  EXPECT_THAT(
+      database_->RetrieveAllCertificates(),
+      UnorderedElementsAre(CertInfoEquals(std::ref(root_cert_info)),
+                           CertInfoEquals(std::ref(intermediate_cert_info))));
+
+  EXPECT_TRUE(
+      database_->DeleteCertificate(intermediate_cert_info.sha256hash_hex));
+
+  EXPECT_THAT(database_->RetrieveAllCertificates(),
+              UnorderedElementsAre(CertInfoEquals(std::ref(root_cert_info))));
+}
+
 TEST(ServerCertificateDatabaseTrustTest, TestTrustMappings) {
   auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
 
diff --git a/chrome/browser/ui/webui/certificate_manager/certificate_manager_handler.cc b/chrome/browser/ui/webui/certificate_manager/certificate_manager_handler.cc
index 730c809..4b319e8f 100644
--- a/chrome/browser/ui/webui/certificate_manager/certificate_manager_handler.cc
+++ b/chrome/browser/ui/webui/certificate_manager/certificate_manager_handler.cc
@@ -197,7 +197,7 @@
             "trusted_certs",
             chrome_browser_server_certificate_database::CertificateTrust::
                 CERTIFICATE_TRUST_TYPE_TRUSTED,
-            profile_);
+            profile_, &remote_client_);
         break;
       case certificate_manager_v2::mojom::CertificateSource::
           kUserIntermediateCerts:
@@ -205,7 +205,7 @@
             "intermediate_certs",
             chrome_browser_server_certificate_database::CertificateTrust::
                 CERTIFICATE_TRUST_TYPE_UNSPECIFIED,
-            profile_);
+            profile_, &remote_client_);
         break;
       case certificate_manager_v2::mojom::CertificateSource::
           kUserDistrustedCerts:
@@ -213,7 +213,7 @@
             "distrusted_certs",
             chrome_browser_server_certificate_database::CertificateTrust::
                 CERTIFICATE_TRUST_TYPE_DISTRUSTED,
-            profile_);
+            profile_, &remote_client_);
         break;
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
       case certificate_manager_v2::mojom::CertificateSource::
diff --git a/chrome/browser/ui/webui/certificate_manager/user_cert_sources.cc b/chrome/browser/ui/webui/certificate_manager/user_cert_sources.cc
index 2759cfc..9cf98a0 100644
--- a/chrome/browser/ui/webui/certificate_manager/user_cert_sources.cc
+++ b/chrome/browser/ui/webui/certificate_manager/user_cert_sources.cc
@@ -7,6 +7,7 @@
 #include <vector>
 
 #include "base/memory/weak_ptr.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/net/server_certificate_database.h"
 #include "chrome/browser/net/server_certificate_database.pb.h"
 #include "chrome/browser/net/server_certificate_database_service.h"
@@ -18,11 +19,13 @@
 #include "chrome/browser/ui/webui/certificate_viewer_webui.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/net/x509_certificate_model.h"
+#include "chrome/grit/generated_resources.h"
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/web_contents.h"
 #include "crypto/sha2.h"
 #include "net/cert/x509_util.h"
 #include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom.h"
+#include "ui/base/l10n/l10n_util.h"
 
 namespace {
 
@@ -39,9 +42,11 @@
     }
     x509_certificate_model::X509CertificateModel model(
         net::x509_util::CreateCryptoBuffer(cert_info.der_cert), "");
+    // TODO(crbug.com/40928765): is_deletable should be set to false if
+    // CACertificateManagementAllowed is set to None.
     cert_infos.push_back(certificate_manager_v2::mojom::SummaryCertInfo::New(
-        model.HashCertSHA256(), model.GetTitle(),
-        /*is_deletable=*/false));
+        cert_info.sha256hash_hex, model.GetTitle(),
+        /*is_deletable=*/true));
   }
   std::move(callback).Run(std::move(cert_infos));
 }
@@ -106,15 +111,49 @@
                               std::move(export_certs), file_name);
 }
 
+void DeleteCertificateResultAsync(
+    CertificateManagerPageHandler::DeleteCertificateCallback callback,
+    bool result) {
+  if (result) {
+    std::move(callback).Run(
+        certificate_manager_v2::mojom::ActionResult::NewSuccess(
+            certificate_manager_v2::mojom::SuccessResult::kSuccess));
+    return;
+  }
+  std::move(callback).Run(certificate_manager_v2::mojom::ActionResult::NewError(
+      "Error deleting certificate"));
+}
+
+void GotDeleteConfirmation(
+    const std::string& sha256hash_hex,
+    CertificateManagerPageHandler::DeleteCertificateCallback callback,
+    base::WeakPtr<Profile> profile,
+    bool confirmed) {
+  if (confirmed && profile) {
+    net::ServerCertificateDatabaseService* server_cert_service =
+        net::ServerCertificateDatabaseServiceFactory::GetForBrowserContext(
+            profile.get());
+    server_cert_service->DeleteCertificate(
+        sha256hash_hex,
+        base::BindOnce(&DeleteCertificateResultAsync, std::move(callback)));
+    return;
+  }
+  std::move(callback).Run(nullptr);
+}
+
 }  // namespace
 
-UserCertSource::UserCertSource(std::string export_file_name,
-                               chrome_browser_server_certificate_database::
-                                   CertificateTrust::CertificateTrustType trust,
-                               raw_ptr<Profile> profile)
+UserCertSource::UserCertSource(
+    std::string export_file_name,
+    chrome_browser_server_certificate_database::CertificateTrust::
+        CertificateTrustType trust,
+    raw_ptr<Profile> profile,
+    mojo::Remote<certificate_manager_v2::mojom::CertificateManagerPage>*
+        remote_client)
     : export_file_name_(std::move(export_file_name)),
       trust_(trust),
-      profile_(profile) {}
+      profile_(profile),
+      remote_client_(remote_client) {}
 
 void UserCertSource::GetCertificateInfos(
     CertificateManagerPageHandler::GetCertificatesCallback callback) {
@@ -149,3 +188,19 @@
   server_cert_service->GetAllCertificates(base::BindOnce(
       &ExportCertificatesAsync, web_contents, trust_, export_file_name_));
 }
+
+void UserCertSource::DeleteCertificate(
+    const std::string& sha256hash_hex,
+    CertificateManagerPageHandler::DeleteCertificateCallback callback) {
+  // TODO(crbug.com/40928765): This should early return if
+  // CACertificateManagementAllowed is set to None.
+  (*remote_client_)
+      ->AskForConfirmation(
+          l10n_util::GetStringFUTF8(
+              IDS_SETTINGS_CERTIFICATE_MANAGER_V2_DELETE_CERT_TITLE,
+              base::UTF8ToUTF16(sha256hash_hex)),
+          l10n_util::GetStringUTF8(
+              IDS_SETTINGS_CERTIFICATE_MANAGER_V2_DELETE_SERVER_CERT_DESCRIPTION),
+          base::BindOnce(&GotDeleteConfirmation, sha256hash_hex,
+                         std::move(callback), profile_->GetWeakPtr()));
+}
diff --git a/chrome/browser/ui/webui/certificate_manager/user_cert_sources.h b/chrome/browser/ui/webui/certificate_manager/user_cert_sources.h
index eb275cb..c473b87 100644
--- a/chrome/browser/ui/webui/certificate_manager/user_cert_sources.h
+++ b/chrome/browser/ui/webui/certificate_manager/user_cert_sources.h
@@ -15,10 +15,13 @@
 
 class UserCertSource : public CertificateManagerPageHandler::CertSource {
  public:
-  UserCertSource(std::string export_file_name,
-                 chrome_browser_server_certificate_database::CertificateTrust::
-                     CertificateTrustType trust,
-                 raw_ptr<Profile> profile);
+  UserCertSource(
+      std::string export_file_name,
+      chrome_browser_server_certificate_database::CertificateTrust::
+          CertificateTrustType trust,
+      raw_ptr<Profile> profile,
+      mojo::Remote<certificate_manager_v2::mojom::CertificateManagerPage>*
+          remote_client);
 
   void GetCertificateInfos(
       CertificateManagerPageHandler::GetCertificatesCallback callback) override;
@@ -30,11 +33,18 @@
   void ExportCertificates(
       base::WeakPtr<content::WebContents> web_contents) override;
 
+  void DeleteCertificate(
+      const std::string& sha256hash_hex,
+      CertificateManagerPageHandler::DeleteCertificateCallback callback)
+      override;
+
  private:
   std::string export_file_name_;
   chrome_browser_server_certificate_database::CertificateTrust::
       CertificateTrustType trust_;
   raw_ptr<Profile> profile_;
+  raw_ptr<mojo::Remote<certificate_manager_v2::mojom::CertificateManagerPage>>
+      remote_client_;
 };
 
 #endif  // CHROME_BROWSER_UI_WEBUI_CERTIFICATE_MANAGER_USER_CERT_SOURCES_H_