This is an automated email from the ASF dual-hosted git repository.

apratim pushed a commit to branch main
in repository 
https://gitbox.apache.org/repos/asf/incubator-resilientdb-resvault.git


The following commit(s) were added to refs/heads/main by this push:
     new fe26861  Added authentication with ResVault
fe26861 is described below

commit fe2686171079f4703f673f5570e87b0926ea4520
Author: Apratim Shukla <[email protected]>
AuthorDate: Tue Oct 1 22:45:46 2024 -0700

    Added authentication with ResVault
---
 public/background.js    | 124 ++++++++++++++++-
 public/content.js       | 345 ++++++++++++++++++++++++++++++++++--------------
 src/pages/Dashboard.jsx |  12 +-
 3 files changed, 375 insertions(+), 106 deletions(-)

diff --git a/public/background.js b/public/background.js
index 631d374..0f09cf3 100644
--- a/public/background.js
+++ b/public/background.js
@@ -1,5 +1,3 @@
-// background.js
-
 let faviconUrls = {};
 
 // Helper functions
@@ -103,6 +101,14 @@ function updateFaviconUrl(tabId, changeInfo, tab) {
 // Add the listener for tab updates
 chrome.tabs.onUpdated.addListener(updateFaviconUrl);
 
+// Function to generate UUID v4
+function generateUUID() {
+    // Public Domain/MIT
+    return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
+      (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 
4).toString(16)
+    );
+}
+
 chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
     if (request.action === "storeKeys") {
         (async function() {
@@ -405,9 +411,6 @@ chrome.runtime.onMessage.addListener(function (request, 
sender, sendResponse) {
                             }
                         `;
 
-                        // Log the mutation for debugging
-                        console.log('Mutation:', mutation);
-
                         const response = await fetch(decryptedUrl, {
                             method: 'POST',
                             headers: {
@@ -440,6 +443,117 @@ chrome.runtime.onMessage.addListener(function (request, 
sender, sendResponse) {
             });
         })();
 
+        return true; // Keep the message channel open for async sendResponse
+    } else if (request.action === 'submitLoginTransaction') {
+        (async function() {
+            console.log('Handling submitLoginTransaction action');
+            console.log('Sender:', sender);
+            let senderUrl = null;
+            if (sender.tab && sender.tab.url) {
+                senderUrl = sender.tab.url;
+            } else if (sender.url) {
+                senderUrl = sender.url;
+            } else if (sender.origin) {
+                senderUrl = sender.origin;
+            } else {
+                console.error('Sender URL is undefined');
+                sendResponse({ success: false, error: 'Cannot determine sender 
URL' });
+                return;
+            }
+            console.log('Sender URL:', senderUrl);
+
+            const domain = getBaseDomain(senderUrl);
+            console.log('Domain:', domain);
+
+            chrome.storage.local.get(['keys', 'connectedNets'], async function 
(result) {
+                const keys = result.keys || {};
+                const connectedNets = result.connectedNets || {};
+                console.log('ConnectedNets:', connectedNets);
+                const net = connectedNets[domain];
+                console.log('Net for domain:', domain, 'is', net);
+
+                if (keys[domain] && keys[domain][net]) {
+                    const { publicKey, privateKey, url, exportedKey } = 
keys[domain][net];
+
+                    try {
+                        // Import the key material from JWK format
+                        const keyMaterial = await crypto.subtle.importKey(
+                            'jwk',
+                            exportedKey,
+                            { name: 'AES-GCM' },
+                            true,
+                            ['encrypt', 'decrypt']
+                        );
+
+                        const decryptedPublicKey = await 
decryptData(publicKey.ciphertext, publicKey.iv, keyMaterial);
+                        const decryptedPrivateKey = await 
decryptData(privateKey.ciphertext, privateKey.iv, keyMaterial);
+                        const decryptedUrl = await decryptData(url.ciphertext, 
url.iv, keyMaterial);
+
+                        // Prepare asset data with current timestamp and 
unique login_transaction_id
+                        const currentTimestamp = Math.floor(Date.now() / 1000);
+
+                        let loginTransactionId = '';
+                        if (crypto.randomUUID) {
+                            loginTransactionId = 
crypto.randomUUID().replace(/[^a-zA-Z0-9]/g, '');
+                        } else {
+                            loginTransactionId = 
generateUUID().replace(/[^a-zA-Z0-9]/g, '');
+                        }
+
+                        const assetData = JSON.stringify({
+                            data: {
+                                login_timestamp: currentTimestamp,
+                                login_transaction_id: loginTransactionId
+                            }
+                        });
+
+                        // Construct the GraphQL mutation
+                        const mutation = `
+                            mutation {
+                                postTransaction(data: {
+                                    operation: "CREATE"
+                                    amount: 1
+                                    signerPublicKey: 
"${escapeGraphQLString(decryptedPublicKey)}"
+                                    signerPrivateKey: 
"${escapeGraphQLString(decryptedPrivateKey)}"
+                                    recipientPublicKey: 
"${escapeGraphQLString(decryptedPublicKey)}"
+                                    asset: """${assetData}"""
+                                }) {
+                                    id
+                                }
+                            }
+                        `;
+
+                        const response = await fetch(decryptedUrl, {
+                            method: 'POST',
+                            headers: {
+                                'Content-Type': 'application/json',
+                            },
+                            body: JSON.stringify({ query: mutation }),
+                        });
+
+                        if (!response.ok) {
+                            throw new Error(`Network response was not ok: 
${response.statusText}`);
+                        }
+
+                        const resultData = await response.json();
+                        if (resultData.errors) {
+                            console.error('GraphQL errors:', 
resultData.errors);
+                            sendResponse({ success: false, errors: 
resultData.errors });
+                        } else {
+                            console.log('Login transaction submitted 
successfully:', resultData.data);
+                            sendResponse({ success: true, data: 
resultData.data });
+                        }
+                    } catch (error) {
+                        console.error('Error submitting login transaction:', 
error);
+                        sendResponse({ success: false, error: error.message });
+                    }
+                } else {
+                    console.error('No keys found for domain:', domain, 'and 
net:', net);
+                    console.log('Available keys:', keys);
+                    sendResponse({ error: "No keys found for domain and net" 
});
+                }
+            });
+        })();
+
         return true; // Keep the message channel open for async sendResponse
     }
 });
\ No newline at end of file
diff --git a/public/content.js b/public/content.js
index 6345305..bc38506 100644
--- a/public/content.js
+++ b/public/content.js
@@ -3,11 +3,10 @@
 // Function to get full hostname from URL
 function getBaseDomain(url) {
   try {
-      const urlObj = new URL(url);
-      return urlObj.hostname;
+    const urlObj = new URL(url);
+    return urlObj.hostname;
   } catch (error) {
-      console.error('Invalid URL:', url);
-      return '';
+    return '';
   }
 }
 
@@ -22,10 +21,12 @@ function sendMessageToPage(request) {
 }
 
 // Add event listener to listen for messages from the web page
-window.addEventListener("message", (event) => {
+window.addEventListener('message', (event) => {
   if (event.source === window) {
-    if (event.data.direction === "commit-page-script") {
+    if (event.data.direction === 'commit') {
       handleCommitOperation(event);
+    } else if (event.data.direction === 'login') {
+      handleLoginOperation(event);
     }
   }
 });
@@ -34,19 +35,168 @@ window.addEventListener("message", (event) => {
 function handleCommitOperation(event) {
   const { amount, data, recipient } = event.data;
 
-  console.log('Received in handleCommitOperation:', { amount, data, recipient 
});
-
   // Ensure the amount is present before proceeding
   if (amount.trim() !== '') {
-    const modal = createOrUpdateModal(amount);
-    setupModalEventListeners(modal, { amount, data, recipient });
+    const modal = createOrUpdateModal(amount, { amount, data, recipient });
+    // Event delegation handles event listeners
   }
 }
 
+// Handle login operation and display the login modal
+function handleLoginOperation(event) {
+  // Create or update the login modal
+  const modal = createOrUpdateLoginModal();
+  // Event delegation handles event listeners
+}
+
+// Create or update the login modal
+function createOrUpdateLoginModal() {
+  let modal = document.getElementById('resVaultLoginModal');
+  const modalContent = generateLoginModalContent();
+
+  if (!modal) {
+    // Create the modal if it doesn't exist
+    modal = document.createElement('div');
+    modal.id = 'resVaultLoginModal';
+    modal.style.cssText = `
+      position: fixed;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      background-color: rgba(0, 0, 0, 0.5);
+      z-index: 1000;
+      display: block;
+    `;
+    modal.innerHTML = modalContent;
+    document.body.appendChild(modal);
+
+    // Attach a single event listener to the modal for event delegation
+    modal.addEventListener('click', async (event) => {
+      if (event.target.id === 'resVaultLoginModalClose') {
+        modal.style.display = 'none';
+      } else if (event.target.id === 'resVaultLoginModalAuthenticate') {
+        const isConnected = await checkConnectionStatus();
+        if (isConnected) {
+          handleLoginTransactionSubmit();
+        } else {
+          alert('Please connect to the website and net in ResVault 
extension.');
+        }
+        modal.style.display = 'none';
+      }
+    });
+  } else {
+    // Update the modal content if it already exists
+    modal.innerHTML = modalContent;
+    modal.style.display = 'block'; // Ensure the modal is visible again
+  }
+
+  return modal;
+}
+
+// Generate the HTML content for the login modal
+function generateLoginModalContent() {
+  return `
+    <div id="resVaultLoginModalContent" style="
+      position: fixed; 
+      top: 50%; 
+      left: 50%; 
+      transform: translate(-50%, -50%); 
+      background-color: #0f0638; 
+      padding: 20px; 
+      border-radius: 10px; 
+      box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+      z-index: 1001;
+      font-family: Poppins, sans-serif;
+      width: 300px;
+    ">
+      <div style="width: calc(100%);
+        padding: 20px;
+        background-color: #291f57;
+        border-radius: 15px;
+        margin-bottom: 20px;
+        box-shadow: 5px 5px 35px -14px rgba(0,0,0,.17); 
+        text-align: center;">
+        <p style="
+          color: #fff; 
+          font-size: 14px; 
+          font-weight: bold;
+          margin: 0;
+        ">
+          Authenticate with Res<span style="color: #47e7ce;">Vault</span>
+        </p>
+      </div>
+
+      <div style="display: flex; justify-content: space-between;">
+        <button id="resVaultLoginModalClose" style="
+          background-color: #291f57;
+          border: 1px #47e7ce solid;
+          color: white; 
+          border: none; 
+          padding: 10px 20px; 
+          border-radius: 5px; 
+          cursor: pointer;
+          width: 50%;
+          box-sizing: border-box;
+          margin-right: 5px;">
+          Close
+        </button>
+        <button id="resVaultLoginModalAuthenticate" style="
+          background: linear-gradient(60deg, #47e7ce, #4fa8c4); 
+          color: white; 
+          border: none; 
+          padding: 10px 20px; 
+          border-radius: 5px; 
+          cursor: pointer;
+          width: 50%;
+          box-sizing: border-box;">
+          Authenticate
+        </button>
+      </div>
+    </div>
+  `;
+}
+
+// Check if the user is connected to the website and net
+function checkConnectionStatus() {
+  return new Promise((resolve) => {
+    const domain = window.location.hostname;
+
+    chrome.storage.local.get(['keys', 'connectedNets'], (result) => {
+      const keys = result.keys || {};
+      const connectedNets = result.connectedNets || {};
+
+      const net = connectedNets[domain];
+      if (net && keys[domain] && keys[domain][net]) {
+        resolve(true);
+      } else {
+        resolve(false);
+      }
+    });
+  });
+}
+
+// Handle login transaction submission and send data to background script
+function handleLoginTransactionSubmit() {
+  chrome.runtime.sendMessage(
+    {
+      action: 'submitLoginTransaction',
+    },
+    (response) => {
+      if (response) {
+        // Send the response to the page script
+        window.postMessage({ type: 'FROM_CONTENT_SCRIPT', data: response }, 
'*');
+      }
+    }
+  );
+}
+
+// Existing functions for commit operation remain unchanged
+
 // Create or update the modal with the necessary transaction details
-function createOrUpdateModal(amount) {
+function createOrUpdateModal(amount, transactionData) {
   let modal = document.getElementById('resVaultModal');
-  const modalContent = generateModalContent("COMMIT", amount);
+  const modalContent = generateModalContent('COMMIT', amount);
 
   if (!modal) {
     // Create the modal if it doesn't exist
@@ -64,90 +214,98 @@ function createOrUpdateModal(amount) {
     `;
     modal.innerHTML = modalContent;
     document.body.appendChild(modal);
+
+    // Attach a single event listener to the modal for event delegation
+    modal.addEventListener('click', (event) => {
+      if (event.target.id === 'resVaultModalClose') {
+        modal.style.display = 'none';
+      } else if (event.target.id === 'resVaultModalSubmit') {
+        handleTransactionSubmit(modal.transactionData); // Use 
modal.transactionData
+        modal.style.display = 'none';
+      }
+    });
   } else {
     // Update the modal content if it already exists
-    const amountDisplay = modal.querySelector("#amountDisplay");
-    if (amountDisplay) {
-      amountDisplay.textContent = amount;
-    } else {
-      // If the amount display element is not found, recreate the modal content
-      modal.innerHTML = modalContent;
-    }
+    modal.innerHTML = modalContent;
     modal.style.display = 'block'; // Ensure the modal is visible again
   }
 
+  // Update transactionData on the modal element
+  modal.transactionData = transactionData;
+
   return modal;
 }
 
-// Generate the HTML content for the modal
+// Generate the HTML content for the commit modal
 function generateModalContent(operation, amount) {
   return `
   <div id="resVaultModalContent" style="
-  position: fixed; 
-  top: 50%; 
-  left: 50%; 
-  transform: translate(-50%, -50%); 
-  background-color: #0f0638; 
-  padding: 20px; 
-  border-radius: 10px; 
-  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
-  z-index: 1001;
-  font-family: Poppins, sans-serif;
-  width: 300px;
-">
-<div style="width: calc(100%);
-  padding: 20px;
-  background-color: #291f57;
-  border-radius: 15px;
-  margin-bottom: 20px;
-  box-shadow: 5px 5px 35px -14px rgba(0,0,0,.17);">
-  <form id="Form" style="margin: auto;">
-      <div style="width: 100%; margin-bottom: 10px;">
+    position: fixed; 
+    top: 50%; 
+    left: 50%; 
+    transform: translate(-50%, -50%); 
+    background-color: #0f0638; 
+    padding: 20px; 
+    border-radius: 10px; 
+    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+    z-index: 1001;
+    font-family: Poppins, sans-serif;
+    width: 300px;
+  ">
+    <div style="width: calc(100%);
+      padding: 20px;
+      background-color: #291f57;
+      border-radius: 15px;
+      margin-bottom: 20px;
+      box-shadow: 5px 5px 35px -14px rgba(0,0,0,.17);">
+      <form id="Form" style="margin: auto;">
+        <div style="width: 100%; margin-bottom: 10px;">
           <div style="padding: 0;
-          width: 100%;
-          margin: 0;
-          overflow: hidden;
-          border-radius: 0px;
-          color: #fff;
-          border-bottom: 1px rgba(255, 255, 255, 0.3) solid;">
-              <p>
+            width: 100%;
+            margin: 0;
+            overflow: hidden;
+            border-radius: 0px;
+            color: #fff;
+            border-bottom: 1px rgba(255, 255, 255, 0.3) solid;">
+            <p>
               Operation: ${operation}
-              </p>
+            </p>
           </div>
-      </div>
-      <div style="width: 100%;
-      margin-bottom: 10px; display: flex; align-items: center; 
justify-content: space-between;">
-          <p style="width: calc(((100% / 3) * 2) - 35px);
-          border: none;
-          background-color: transparent;
-          padding: 10px 0px;
-          border-radius: 0px;
-          border-bottom: 1px rgba(255, 255, 255, 0.3) solid;
-          color: #fff;
-          text-align: center;
-          ">
-          ${amount}
+        </div>
+        <div style="width: 100%;
+          margin-bottom: 10px; display: flex; align-items: center; 
justify-content: space-between;">
+          <p id="amountDisplay" style="width: calc(((100% / 3) * 2) - 35px);
+            border: none;
+            background-color: transparent;
+            padding: 10px 0px;
+            border-radius: 0px;
+            border-bottom: 1px rgba(255, 255, 255, 0.3) solid;
+            color: #fff;
+            text-align: center;
+            ">
+            ${amount}
           </p>
           <div style="display: flex;
-          align-items: center;"><div style="display: block; font-family: 
Arial, Helvetica, sans-serif; font-size: 20px; font-weight: bold; color: 
#f0f0f0; background-color: #808080; border-radius: 50%; width: 30px; height: 
30px; display: flex; align-items: center; justify-content: center; margin: 
10px; transform: rotate(20deg);">R</div><span style="color: #fff; font-weight: 
600; padding-left: 5px;
-          ">RoK</span></div>
-      </div>
-      <p style="font-size: small; color: #fff">Powered by Res<strong 
style="color: #47e7ce">Vault</strong></p>
-      </div>
-  </form>
-  <span style="display: flex;">
-      <button id="resVaultModalClose" style="
+            align-items: center;">
+            <div style="display: block; font-family: Arial, Helvetica, 
sans-serif; font-size: 20px; font-weight: bold; color: #f0f0f0; 
background-color: #808080; border-radius: 50%; width: 30px; height: 30px; 
display: flex; align-items: center; justify-content: center; margin: 10px; 
transform: rotate(20deg);">R</div>
+            <span style="color: #fff; font-weight: 600; padding-left: 
5px;">RoK</span>
+          </div>
+        </div>
+        <p style="font-size: small; color: #fff">Powered by Res<strong 
style="color: #47e7ce">Vault</strong></p>
+      </form>
+    </div>
+    <span style="display: flex;">
+        <button id="resVaultModalClose" style="
           background-color: #291f57;
-          border: 1px #47e7ce solid;
+          border: none;
           color: white; 
-          border: none; 
           padding: 10px 20px; 
           border-radius: 5px; 
           cursor: pointer;
           width: 50%;
           box-sizing: border-box;
           margin-right: 5px;">Cancel</button>
-      <button id="resVaultModalSubmit" style="
+        <button id="resVaultModalSubmit" style="
           background: linear-gradient(60deg, #47e7ce, #4fa8c4); 
           color: white; 
           border: none; 
@@ -156,37 +314,24 @@ function generateModalContent(operation, amount) {
           cursor: pointer;
           width: 50%;
           box-sizing: border-box;">Submit</button>
-  </span>                       
-</div>
-</div>`;
-}
-
-// Setup event listeners for modal interactions
-function setupModalEventListeners(modal, transactionData) {
-  const closeModalButton = modal.querySelector('#resVaultModalClose');
-  const submitButton = modal.querySelector('#resVaultModalSubmit');
-
-  closeModalButton.addEventListener('click', () => {
-    modal.style.display = 'none';
-  });
-
-  submitButton.addEventListener('click', () => {
-    handleTransactionSubmit(transactionData);
-    modal.style.display = 'none';
-  });
+    </span>                       
+  </div>`;
 }
 
 // Handle transaction submission and send data to background script
 function handleTransactionSubmit({ amount, data, recipient }) {
-  chrome.runtime.sendMessage({
-    action: 'submitTransaction',
-    amount,
-    data,
-    recipient
-  }, (response) => {
-    if (response) {
-      // Send the response to the page script
-      window.postMessage({ type: 'FROM_CONTENT_SCRIPT', data: response }, '*');
+  chrome.runtime.sendMessage(
+    {
+      action: 'submitTransaction',
+      amount,
+      data,
+      recipient,
+    },
+    (response) => {
+      if (response) {
+        // Send the response to the page script
+        window.postMessage({ type: 'FROM_CONTENT_SCRIPT', data: response }, 
'*');
+      }
     }
-  });
+  );
 }
\ No newline at end of file
diff --git a/src/pages/Dashboard.jsx b/src/pages/Dashboard.jsx
index caf28f1..ce52a3c 100644
--- a/src/pages/Dashboard.jsx
+++ b/src/pages/Dashboard.jsx
@@ -490,6 +490,11 @@ function Dashboard() {
         }
     };
 
+    // **New function to handle favicon load error**
+    const handleFaviconError = () => {
+        setFaviconUrl(''); // This will trigger the globe icon to display
+    };
+
     return (
         <>
             <div className="lottie-background">
@@ -624,7 +629,12 @@ function Dashboard() {
 
                             <div className="icon-container" 
onClick={toggleConnection}>
                                 {faviconUrl ? (
-                                    <img src={faviconUrl} alt="Favicon" 
className={`icon ${isConnected ? 'connected' : ''}`} />
+                                    <img
+                                        src={faviconUrl}
+                                        alt="Favicon"
+                                        className={`icon ${isConnected ? 
'connected' : ''}`}
+                                        onError={handleFaviconError} // Add 
onError handler
+                                    />
                                 ) : (
                                     <i className={`fa fa-globe icon 
${isConnected ? 'connected' : ''}`} aria-hidden="true"></i>
                                 )}

Reply via email to