mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-03-18 22:57:01 -04:00
Extract service icons during both credential create and update (#771)
This commit is contained in:
@@ -123,8 +123,9 @@ export class EncryptionUtility {
|
||||
*/
|
||||
public static async generateRsaKeyPair(): Promise<{ publicKey: string, privateKey: string }> {
|
||||
/**
|
||||
* TODO: ensure the key pair is generated in the correct format where private key is in expected
|
||||
* JWK format that the WASM app already outputs.
|
||||
* TODO: this method is currently unused. When we enable the browser extension to actually generate keys,
|
||||
* check if the key pair is generated in the correct format where private key is in expected JWK format
|
||||
* that the WASM app already outputs.
|
||||
*/
|
||||
const keyPair = await crypto.subtle.generateKey(
|
||||
{
|
||||
|
||||
@@ -120,8 +120,8 @@ export default function AddEditCredentialScreen() {
|
||||
}
|
||||
|
||||
// Assemble the credential to save
|
||||
let credentialToSave = {
|
||||
Id: isEditMode ? id : undefined,
|
||||
let credentialToSave: Credential = {
|
||||
Id: isEditMode ? id : '',
|
||||
Username: watch('Username'),
|
||||
Password: watch('Password'),
|
||||
ServiceName: watch('ServiceName'),
|
||||
@@ -139,28 +139,28 @@ export default function AddEditCredentialScreen() {
|
||||
// Convert user birthdate entry format (yyyy-mm-dd) into valid ISO 8601 format for database storage
|
||||
credentialToSave.Alias.BirthDate = IdentityHelperUtils.normalizeBirthDateForDb(credentialToSave.Alias.BirthDate);
|
||||
|
||||
// Extract favicon from service URL if the credential has one
|
||||
if (credentialToSave.ServiceUrl) {
|
||||
try {
|
||||
const timeoutPromise = new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('Favicon extraction timed out')), 5000)
|
||||
);
|
||||
|
||||
const faviconPromise = webApi.get<FaviconExtractModel>('Favicon/Extract?url=' + credentialToSave.ServiceUrl);
|
||||
const faviconResponse = await Promise.race([faviconPromise, timeoutPromise]) as FaviconExtractModel;
|
||||
if (faviconResponse?.image) {
|
||||
const decodedImage = Uint8Array.from(Buffer.from(faviconResponse.image as string, 'base64'));
|
||||
credentialToSave.Logo = decodedImage;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Favicon extraction failed or timed out:', error);
|
||||
}
|
||||
}
|
||||
|
||||
await executeVaultMutation(async () => {
|
||||
if (isEditMode) {
|
||||
await dbContext.sqliteClient!.updateCredentialById(credentialToSave);
|
||||
} else {
|
||||
// For new credentials, try to extract favicon
|
||||
if (credentialToSave.ServiceUrl) {
|
||||
try {
|
||||
const timeoutPromise = new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('Favicon extraction timed out')), 5000)
|
||||
);
|
||||
|
||||
const faviconPromise = webApi.get<FaviconExtractModel>('Favicon/Extract?url=' + credentialToSave.ServiceUrl);
|
||||
const faviconResponse = await Promise.race([faviconPromise, timeoutPromise]) as FaviconExtractModel;
|
||||
if (faviconResponse?.image) {
|
||||
const decodedImage = Uint8Array.from(Buffer.from(faviconResponse.image as string, 'base64'));
|
||||
credentialToSave.Logo = decodedImage;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Favicon extraction failed or timed out:', error);
|
||||
}
|
||||
}
|
||||
|
||||
const credentialId = await dbContext.sqliteClient!.createCredential(credentialToSave);
|
||||
credentialToSave.Id = credentialId;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class CredentialIdentityStore {
|
||||
guard let username = credential.username, !username.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
let effectiveDomain = Self.effectiveDomain(from: host)
|
||||
|
||||
return ASPasswordCredentialIdentity(
|
||||
@@ -43,7 +43,7 @@ class CredentialIdentityStore {
|
||||
print("Credential identity store is not enabled.")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
do {
|
||||
try await store.saveCredentialIdentities(identities)
|
||||
} catch {
|
||||
@@ -65,14 +65,13 @@ class CredentialIdentityStore {
|
||||
return ASPasswordCredentialIdentity(
|
||||
serviceIdentifier: serviceIdentifier,
|
||||
user: credential.username ?? "",
|
||||
// TODO: Use the actual record identifier when implementing the actual vault
|
||||
recordIdentifier: UUID().uuidString
|
||||
recordIdentifier: credential.id.uuidString
|
||||
)
|
||||
}
|
||||
|
||||
try await store.removeCredentialIdentities(identities)
|
||||
}
|
||||
|
||||
|
||||
private func storeState() async -> ASCredentialIdentityStoreState {
|
||||
await withCheckedContinuation { continuation in
|
||||
store.getState { state in
|
||||
@@ -80,7 +79,7 @@ class CredentialIdentityStore {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static func effectiveDomain(from host: String) -> String {
|
||||
let parts = host.split(separator: ".")
|
||||
guard parts.count >= 2 else { return host }
|
||||
|
||||
@@ -110,8 +110,8 @@ class EncryptionUtility {
|
||||
* Generates a new RSA key pair for asymmetric encryption
|
||||
*/
|
||||
public static async generateRsaKeyPair(): Promise<{ publicKey: string, privateKey: string }> {
|
||||
// TODO: ensure the key pair is generated in the correct format where private key is in expected
|
||||
// JWK format that the WASM app already outputs.
|
||||
// TODO: this method is currently unused. When we enable the app to actually generate keys, check if the key pair is
|
||||
// generated in the correct format where private key is in expected JWK format that the WASM app already outputs.
|
||||
const keyPair = await crypto.subtle.generateKey(
|
||||
{
|
||||
name: "RSA-OAEP",
|
||||
|
||||
@@ -559,7 +559,7 @@ class SqliteClient {
|
||||
try {
|
||||
/*
|
||||
* Check if TotpCodes table exists (for backward compatibility).
|
||||
* TODO: whenever the browser extension has a minimum client DB version of 1.5.0+,
|
||||
* TODO: whenever the mobile app has a minimum client DB version of 1.5.0+,
|
||||
* we can remove this check as the TotpCodes table then is guaranteed to exist.
|
||||
*/
|
||||
if (!await this.tableExists('TotpCodes')) {
|
||||
@@ -679,13 +679,11 @@ class SqliteClient {
|
||||
}
|
||||
|
||||
// 1. Update Service
|
||||
// TODO: make Logo update optional, currently not supported as its becoming null.
|
||||
// Logo = ?,
|
||||
|
||||
const serviceQuery = `
|
||||
UPDATE Services
|
||||
SET Name = ?,
|
||||
Url = ?,
|
||||
Logo = COALESCE(?, Logo),
|
||||
UpdatedAt = ?
|
||||
WHERE Id = (
|
||||
SELECT ServiceId
|
||||
@@ -693,12 +691,14 @@ class SqliteClient {
|
||||
WHERE Id = ?
|
||||
)`;
|
||||
|
||||
/*let logoData = null;
|
||||
let logoData = null;
|
||||
try {
|
||||
if (credential.Logo) {
|
||||
// Handle object-like array conversion
|
||||
if (typeof credential.Logo === 'object' && !ArrayBuffer.isView(credential.Logo)) {
|
||||
const values = Object.values(credential.Logo);
|
||||
logoData = new Uint8Array(values);
|
||||
// Handle existing array types
|
||||
} else if (Array.isArray(credential.Logo) || credential.Logo instanceof ArrayBuffer || credential.Logo instanceof Uint8Array) {
|
||||
logoData = new Uint8Array(credential.Logo);
|
||||
}
|
||||
@@ -706,12 +706,12 @@ class SqliteClient {
|
||||
} catch (error) {
|
||||
console.warn('Failed to convert logo to Uint8Array:', error);
|
||||
logoData = null;
|
||||
}*/
|
||||
}
|
||||
|
||||
await this.executeUpdate(serviceQuery, [
|
||||
credential.ServiceName,
|
||||
credential.ServiceUrl ?? null,
|
||||
//logoData,
|
||||
logoData,
|
||||
currentDateTime,
|
||||
credential.Id
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user