Add single credential retrieve query (#541)

This commit is contained in:
Leendert de Borst
2025-02-06 12:14:47 +01:00
parent 826037d499
commit f894476e0e
7 changed files with 76 additions and 25 deletions

View File

@@ -25,7 +25,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
handleClearVault(vaultState, sendResponse);
break;
case 'GET_CREDENTIALS_FOR_URL':
case 'GET_CREDENTIALS':
handleGetCredentials(vaultState, sendResponse);
break;

View File

@@ -52,12 +52,11 @@ const CredentialDetails: React.FC = () => {
if (!dbContext?.sqliteClient || !id) return;
try {
const result = dbContext.sqliteClient.getAllCredentials();
// TODO: create a SQLite function to get a credential by id.
const credential = result.find(cred => cred.Id === id);
if (credential) {
setCredential(credential);
const result = dbContext.sqliteClient.getCredentialById(id);
if (result) {
setCredential(result);
} else {
console.error('Credential not found');
navigate('/credentials');
}
} catch (err) {

View File

@@ -38,6 +38,7 @@ const EmailsList: React.FC = () => {
return;
}
// TODO: create separate query to only get email addresses to avoid loading all credentials.
const credentials = dbContext.sqliteClient.getAllCredentials();
// Get unique email addresses from all credentials.

View File

@@ -73,20 +73,11 @@ const Login: React.FC = () => {
// Initialize the SQLite context with the new vault data.
await dbContext.initializeDatabase(vaultResponseJson, passwordHashBase64);
// 3. Handle 2FA if required
/*
* if (validationResponse.requiresTwoFactor) {
* // TODO: Implement 2FA flow
* console.log('2FA required');
* return;
* }
*/
// 3. TODO: Handle 2FA if required
// 5. Redirect to home page
/*
* window.location.href = '/';
*/
// 4. TODO: handle recovery code if required (or link to main client instead)
// Show app.
hideLoading();
} catch (err) {
setError('Login failed. Please check your credentials and try again.');

View File

@@ -104,7 +104,7 @@ export function handleClearVault(
}
/**
* Get the credentials for a URL.
* Get all credentials.
*/
export async function handleGetCredentials(
vaultState: VaultState,
@@ -191,6 +191,7 @@ export function getEmailAddressesForVault(
sqliteClient: SqliteClient,
vaultState: VaultState
): string[] {
// TODO: create separate query to only get email addresses to avoid loading all credentials.
const credentials = sqliteClient.getAllCredentials();
const emailAddresses = credentials

View File

@@ -280,7 +280,7 @@ export function createAutofillPopup(input: HTMLInputElement, credentials: Creden
const searchTerm = searchInput.value.toLowerCase();
// Request credentials from background script
chrome.runtime.sendMessage({ type: 'GET_CREDENTIALS_FOR_URL', url: window.location.href }, (response: CredentialResponse) => {
chrome.runtime.sendMessage({ type: 'GET_CREDENTIALS' }, (response: CredentialResponse) => {
if (response.status === 'OK' && response.credentials) {
let filteredCredentials;
@@ -862,7 +862,7 @@ export function openAutofillPopup(input: HTMLInputElement) : void {
document.addEventListener('keydown', handleEnterKey);
// Request credentials from background script
chrome.runtime.sendMessage({ type: 'GET_CREDENTIALS_FOR_URL', url: window.location.href }, (response: CredentialResponse) => {
chrome.runtime.sendMessage({ type: 'GET_CREDENTIALS' }, (response: CredentialResponse) => {
switch (response.status) {
case 'OK':
if (response.credentials?.length) {

View File

@@ -77,7 +77,8 @@ class SqliteClient {
const results: T[] = [];
while (stmt.step()) {
results.push(stmt.getAsObject());
// eslint-disable-next-line @typescript-eslint/no-explicit-any
results.push(stmt.getAsObject() as any);
}
stmt.free();
@@ -110,7 +111,7 @@ class SqliteClient {
}
/**
* Close the database connection and free resources
* Close the database connection and free resources.
*/
public close(): void {
if (this.db) {
@@ -120,8 +121,66 @@ class SqliteClient {
}
/**
* Fetch all credentials with their associated service information
* @returns Array of Credential objects with service details
* Fetch a single credential with its associated service information.
* @param credentialId - The ID of the credential to fetch.
* @returns Credential object with service details or null if not found.
*/
public getCredentialById(credentialId: string): Credential | null {
const query = `
SELECT DISTINCT
c.Id,
c.Username,
c.Notes,
c.ServiceId,
s.Name as ServiceName,
s.Url as ServiceUrl,
s.Logo as Logo,
a.FirstName,
a.LastName,
a.NickName,
a.BirthDate,
a.Gender,
a.Email,
p.Value as Password
FROM Credentials c
LEFT JOIN Services s ON c.ServiceId = s.Id
LEFT JOIN Aliases a ON c.AliasId = a.Id
LEFT JOIN Passwords p ON p.CredentialId = c.Id
WHERE c.IsDeleted = 0
AND c.Id = ?`;
const results = this.executeQuery(query, [credentialId]);
if (results.length === 0) {
return null;
}
// Convert the first row to a Credential object
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const row = results[0] as any;
return {
Id: row.Id,
Username: row.Username,
Password: row.Password,
Email: row.Email,
ServiceName: row.ServiceName,
ServiceUrl: row.ServiceUrl,
Logo: row.Logo,
Notes: row.Notes,
Alias: {
FirstName: row.FirstName,
LastName: row.LastName,
NickName: row.NickName,
BirthDate: row.BirthDate,
Gender: row.Gender,
Email: row.Email
}
};
}
/**
* Fetch all credentials with their associated service information.
* @returns Array of Credential objects with service details.
*/
public getAllCredentials(): Credential[] {
const query = `