app: event to pass client certificate data
authordeepak1556 <hop2deep@gmail.com>
Thu, 11 Jun 2015 15:22:07 +0000 (20:52 +0530)
committerdeepak1556 <hop2deep@gmail.com>
Thu, 25 Jun 2015 15:59:21 +0000 (21:29 +0530)
atom/browser/api/atom_api_app.cc
atom/browser/api/atom_api_app.h
atom/browser/atom_browser_client.cc
atom/browser/browser.cc
atom/browser/browser.h
atom/browser/browser_observer.h
docs/api/app.md

index 473111d..036c61b 100644 (file)
 #include "atom/browser/atom_browser_context.h"
 #include "atom/browser/atom_browser_main_parts.h"
 #include "atom/browser/browser.h"
+#include "atom/browser/api/atom_api_web_contents.h"
 #include "atom/common/native_mate_converters/file_path_converter.h"
 #include "base/command_line.h"
 #include "base/environment.h"
 #include "base/files/file_path.h"
 #include "base/path_service.h"
 #include "brightray/browser/brightray_paths.h"
+#include "content/public/browser/client_certificate_delegate.h"
 #include "native_mate/callback.h"
 #include "native_mate/dictionary.h"
 #include "native_mate/object_template_builder.h"
+#include "net/ssl/ssl_cert_request_info.h"
 
 #if defined(OS_WIN)
 #include "base/strings/utf_string_conversions.h"
@@ -57,6 +60,21 @@ struct Converter<Browser::UserTask> {
 };
 #endif
 
+template<>
+struct Converter<scoped_refptr<net::X509Certificate>> {
+  static v8::Local<v8::Value> ToV8(
+      v8::Isolate* isolate,
+      const scoped_refptr<net::X509Certificate>& val) {
+    mate::Dictionary dict(isolate, v8::Object::New(isolate));
+    std::string encoded_data;
+    net::X509Certificate::GetPEMEncoded(
+        val->os_cert_handle(), &encoded_data);
+    dict.Set("data", encoded_data);
+    dict.Set("issuerName", val->issuer().GetDisplayName());
+    return dict.GetHandle();
+  }
+};
+
 }  // namespace mate
 
 
@@ -90,6 +108,29 @@ int GetPathConstant(const std::string& name) {
     return -1;
 }
 
+void OnClientCertificateSelected(
+    v8::Isolate* isolate,
+    std::shared_ptr<content::ClientCertificateDelegate> delegate,
+    mate::Arguments* args) {
+  v8::Locker locker(isolate);
+  v8::HandleScope handle_scope(isolate);
+  mate::Dictionary cert_data;
+  if (!(args->Length() == 1 && args->GetNext(&cert_data))) {
+    args->ThrowError();
+    return;
+  }
+
+  std::string encoded_data;
+  cert_data.Get("data", &encoded_data);
+
+  auto certs =
+      net::X509Certificate::CreateCertificateListFromBytes(
+          encoded_data.data(), encoded_data.size(),
+          net::X509Certificate::FORMAT_AUTO);
+
+  delegate->ContinueWithCertificate(certs[0].get());
+}
+
 }  // namespace
 
 App::App() {
@@ -144,6 +185,27 @@ void App::OnFinishLaunching() {
   Emit("ready");
 }
 
+void App::OnSelectCertificate(
+    content::WebContents* web_contents,
+    net::SSLCertRequestInfo* cert_request_info,
+    scoped_ptr<content::ClientCertificateDelegate> delegate) {
+  std::shared_ptr<content::ClientCertificateDelegate>
+      shared_delegate(delegate.release());
+  bool prevent_default =
+      Emit("select-certificate",
+           api::WebContents::CreateFrom(isolate(), web_contents),
+           cert_request_info->host_and_port.ToString(),
+           cert_request_info->client_certs,
+           base::Bind(&OnClientCertificateSelected,
+                      isolate(),
+                      shared_delegate));
+
+  // Default to first certificate from the platform store.
+  if (!prevent_default)
+    shared_delegate->ContinueWithCertificate(
+        cert_request_info->client_certs[0].get());
+}
+
 base::FilePath App::GetPath(mate::Arguments* args, const std::string& name) {
   bool succeed = false;
   base::FilePath path;
index f304175..9748991 100644 (file)
@@ -42,6 +42,10 @@ class App : public mate::EventEmitter,
   void OnActivateWithNoOpenWindows() override;
   void OnWillFinishLaunching() override;
   void OnFinishLaunching() override;
+  void OnSelectCertificate(
+      content::WebContents* web_contents,
+      net::SSLCertRequestInfo* cert_request_info,
+      scoped_ptr<content::ClientCertificateDelegate> delegate) override;
 
   // mate::Wrappable:
   mate::ObjectTemplateBuilder GetObjectTemplateBuilder(
index 6c29c16..85e19cf 100644 (file)
@@ -9,6 +9,7 @@
 #include "atom/browser/atom_browser_main_parts.h"
 #include "atom/browser/atom_quota_permission_context.h"
 #include "atom/browser/atom_speech_recognition_manager_delegate.h"
+#include "atom/browser/browser.h"
 #include "atom/browser/native_window.h"
 #include "atom/browser/web_view_manager.h"
 #include "atom/browser/window_list.h"
@@ -231,15 +232,13 @@ void AtomBrowserClient::SelectClientCertificate(
   auto cert_path = command_line->GetSwitchValueNative(
       switches::kClientCertificate);
 
-  // TODO(zcbenz): allow users to select certificate from
-  // client_cert list. Right now defaults to first certificate
-  // in the list.
   scoped_refptr<net::X509Certificate> certificate;
-  if (cert_path.empty()) {
-    if (!cert_request_info->client_certs.empty())
-      certificate = cert_request_info->client_certs[0];
-  } else {
+  if (!cert_path.empty()) {
     certificate = ImportCertFromFile(base::FilePath(cert_path));
+  } else if (!cert_request_info->client_certs.empty()) {
+    Browser::Get()->ClientCertificateSelector(web_contents,
+                                              cert_request_info,
+                                              delegate.Pass());
   }
 
   if (certificate.get())
index 123cfc4..1ca682d 100644 (file)
@@ -9,6 +9,8 @@
 #include "atom/browser/atom_browser_main_parts.h"
 #include "atom/browser/window_list.h"
 #include "base/message_loop/message_loop.h"
+#include "content/public/browser/client_certificate_delegate.h"
+#include "net/ssl/ssl_cert_request_info.h"
 
 namespace atom {
 
@@ -104,6 +106,17 @@ void Browser::DidFinishLaunching() {
   FOR_EACH_OBSERVER(BrowserObserver, observers_, OnFinishLaunching());
 }
 
+void Browser::ClientCertificateSelector(
+    content::WebContents* web_contents,
+    net::SSLCertRequestInfo* cert_request_info,
+    scoped_ptr<content::ClientCertificateDelegate> delegate) {
+  FOR_EACH_OBSERVER(BrowserObserver,
+                    observers_,
+                    OnSelectCertificate(web_contents,
+                                        cert_request_info,
+                                        delegate.Pass()));
+}
+
 void Browser::NotifyAndShutdown() {
   bool prevent_default = false;
   FOR_EACH_OBSERVER(BrowserObserver, observers_, OnWillQuit(&prevent_default));
index 8648d88..3e93c84 100644 (file)
@@ -115,6 +115,12 @@ class Browser : public WindowListObserver {
   void WillFinishLaunching();
   void DidFinishLaunching();
 
+  // Called when client certificate is required.
+  void ClientCertificateSelector(
+      content::WebContents* web_contents,
+      net::SSLCertRequestInfo* cert_request_info,
+      scoped_ptr<content::ClientCertificateDelegate> delegate);
+
   void AddObserver(BrowserObserver* obs) {
     observers_.AddObserver(obs);
   }
index 4f9f436..19f4bd8 100644 (file)
@@ -7,6 +7,17 @@
 
 #include <string>
 
+#include "base/memory/scoped_ptr.h"
+
+namespace content {
+class ClientCertificateDelegate;
+class WebContents;
+}
+
+namespace net {
+class SSLCertRequestInfo;
+}
+
 namespace atom {
 
 class BrowserObserver {
@@ -40,6 +51,12 @@ class BrowserObserver {
   virtual void OnWillFinishLaunching() {}
   virtual void OnFinishLaunching() {}
 
+  // The browser requires client certificate.
+  virtual void OnSelectCertificate(
+      content::WebContents* web_contents,
+      net::SSLCertRequestInfo* cert_request_info,
+      scoped_ptr<content::ClientCertificateDelegate> delegate) {}
+
  protected:
   virtual ~BrowserObserver() {}
 };
index 1564196..d1b219c 100644 (file)
@@ -101,6 +101,30 @@ Emitted when a [browserWindow](browser-window.md) gets blurred.
 
 Emitted when a [browserWindow](browser-window.md) gets focused.
 
+### Event: 'select-certificate'
+
+Emitted when client certificate is requested.
+
+* `event` Event
+* `webContents` [WebContents](browser-window.md#class-webcontents)
+* `url` String
+* `certificateList` [Objects]
+  * `data` PEM encoded data
+  * `issuerName` Issuer's Common Name
+* `callback` Function
+
+```
+app.on('select-certificate', function(event, host, url, list, callback) {
+  event.preventDefault();
+  callback(list[0]);
+})
+```
+
+`url` corresponds to the navigation entry requesting the client certificate,
+`callback` needs to be called with an entry filtered from the list.
+`event.preventDefault()` prevents from using the first certificate from
+the store.
+
 ## app.quit()
 
 Try to close all windows. The `before-quit` event will first be emitted. If all