196 lines
5.1 KiB
JavaScript
196 lines
5.1 KiB
JavaScript
const ext = globalThis.browser ?? globalThis.chrome;
|
|
const nativeHost = "org.keepassgo.browser";
|
|
const defaultSettings = {
|
|
grpcAddress: "127.0.0.1:47777",
|
|
bearerToken: ""
|
|
};
|
|
|
|
function storageGet(keys) {
|
|
return new Promise((resolve, reject) => {
|
|
ext.storage.local.get(keys, (value) => {
|
|
const error = ext.runtime.lastError;
|
|
if (error) {
|
|
reject(new Error(error.message));
|
|
return;
|
|
}
|
|
resolve(value);
|
|
});
|
|
});
|
|
}
|
|
|
|
function storageSet(value) {
|
|
return new Promise((resolve, reject) => {
|
|
ext.storage.local.set(value, () => {
|
|
const error = ext.runtime.lastError;
|
|
if (error) {
|
|
reject(new Error(error.message));
|
|
return;
|
|
}
|
|
resolve();
|
|
});
|
|
});
|
|
}
|
|
|
|
function tabsQuery(query) {
|
|
return new Promise((resolve, reject) => {
|
|
ext.tabs.query(query, (tabs) => {
|
|
const error = ext.runtime.lastError;
|
|
if (error) {
|
|
reject(new Error(error.message));
|
|
return;
|
|
}
|
|
resolve(tabs);
|
|
});
|
|
});
|
|
}
|
|
|
|
function tabsSendMessage(tabId, message) {
|
|
return new Promise((resolve, reject) => {
|
|
ext.tabs.sendMessage(tabId, message, (response) => {
|
|
const error = ext.runtime.lastError;
|
|
if (error) {
|
|
reject(new Error(error.message));
|
|
return;
|
|
}
|
|
resolve(response);
|
|
});
|
|
});
|
|
}
|
|
|
|
function connectNative(message) {
|
|
return new Promise((resolve, reject) => {
|
|
ext.runtime.sendNativeMessage(nativeHost, message, (response) => {
|
|
const error = ext.runtime.lastError;
|
|
if (error) {
|
|
reject(new Error(error.message));
|
|
return;
|
|
}
|
|
resolve(response);
|
|
});
|
|
});
|
|
}
|
|
|
|
async function loadSettings() {
|
|
const stored = await storageGet(["grpcAddress", "bearerToken"]);
|
|
return {
|
|
grpcAddress: (stored.grpcAddress || defaultSettings.grpcAddress).trim(),
|
|
bearerToken: (stored.bearerToken || "").trim()
|
|
};
|
|
}
|
|
|
|
async function activePageContext() {
|
|
const [tab] = await tabsQuery({ active: true, currentWindow: true });
|
|
return {
|
|
tabId: tab?.id ?? null,
|
|
url: typeof tab?.url === "string" ? tab.url : ""
|
|
};
|
|
}
|
|
|
|
async function statusForPage() {
|
|
const settings = await loadSettings();
|
|
const page = await activePageContext();
|
|
if (!settings.bearerToken) {
|
|
return {
|
|
success: false,
|
|
configured: false,
|
|
status: null,
|
|
pageUrl: page.url,
|
|
matches: [],
|
|
error: "Set an API token in extension settings."
|
|
};
|
|
}
|
|
|
|
const status = await connectNative({
|
|
action: "status",
|
|
grpcAddress: settings.grpcAddress,
|
|
bearerToken: settings.bearerToken
|
|
});
|
|
if (!status.success || status.status?.locked || !page.url.startsWith("http")) {
|
|
return {
|
|
success: status.success,
|
|
configured: true,
|
|
status: status.status ?? null,
|
|
pageUrl: page.url,
|
|
matches: [],
|
|
error: status.error ?? ""
|
|
};
|
|
}
|
|
|
|
const matches = await connectNative({
|
|
action: "find-logins",
|
|
grpcAddress: settings.grpcAddress,
|
|
bearerToken: settings.bearerToken,
|
|
url: page.url
|
|
});
|
|
return {
|
|
success: matches.success,
|
|
configured: true,
|
|
status: matches.status ?? status.status ?? null,
|
|
pageUrl: page.url,
|
|
matches: matches.matches ?? [],
|
|
error: matches.error ?? ""
|
|
};
|
|
}
|
|
|
|
async function fillLogin(entryId) {
|
|
const settings = await loadSettings();
|
|
const page = await activePageContext();
|
|
if (!settings.bearerToken) {
|
|
throw new Error("API token is not configured.");
|
|
}
|
|
if (page.tabId == null) {
|
|
throw new Error("No active tab is available.");
|
|
}
|
|
|
|
const response = await connectNative({
|
|
action: "get-login",
|
|
grpcAddress: settings.grpcAddress,
|
|
bearerToken: settings.bearerToken,
|
|
entryId,
|
|
url: page.url
|
|
});
|
|
if (!response.success || !response.credential) {
|
|
throw new Error(response.error || "KeePassGO did not return a credential.");
|
|
}
|
|
|
|
const fillResponse = await tabsSendMessage(page.tabId, {
|
|
type: "keepassgo-fill-credential",
|
|
credential: response.credential
|
|
});
|
|
if (!fillResponse?.ok) {
|
|
throw new Error(fillResponse?.error || "The current page could not be filled.");
|
|
}
|
|
return {
|
|
credential: response.credential,
|
|
pageUrl: page.url
|
|
};
|
|
}
|
|
|
|
ext.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
|
(async () => {
|
|
switch (message?.type) {
|
|
case "keepassgo-popup-state":
|
|
sendResponse(await statusForPage());
|
|
return;
|
|
case "keepassgo-fill-entry":
|
|
sendResponse({ success: true, ...(await fillLogin(message.entryId)) });
|
|
return;
|
|
case "keepassgo-load-settings":
|
|
sendResponse({ success: true, settings: await loadSettings() });
|
|
return;
|
|
case "keepassgo-save-settings":
|
|
await storageSet({
|
|
grpcAddress: String(message.settings?.grpcAddress || defaultSettings.grpcAddress).trim(),
|
|
bearerToken: String(message.settings?.bearerToken || "").trim()
|
|
});
|
|
sendResponse({ success: true });
|
|
return;
|
|
default:
|
|
sendResponse({ success: false, error: `Unsupported message ${message?.type || ""}`.trim() });
|
|
}
|
|
})().catch((error) => {
|
|
sendResponse({ success: false, error: error instanceof Error ? error.message : String(error) });
|
|
});
|
|
return true;
|
|
});
|