This repository has been archived by the owner on Oct 19, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
262 additions
and
133 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
<div class="transparent-900 loadSavedCodes m-auto my-20 w-4/5 rounded-2xl p-10 text-left"> | ||
<h1 class="px-10">Load saved codes</h1> | ||
<div class="mx-auto flex flex-col items-center justify-center rounded-2xl p-10"> | ||
<div class="transparent-800 mb-5 flex w-full flex-row items-center justify-between rounded-xl p-5 text-left"> | ||
<div> | ||
<h2>Loading saved codes</h2> | ||
<h3>Load your saved 2FA codes. You can edit or delete your codes, or you can even add new codes here.</h3> | ||
</div> | ||
<div class="ml-20 flex gap-3"> | ||
<button class="button" on:click={loadSavedCodes}> | ||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"> | ||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /> | ||
</svg> | ||
Confirm | ||
</button> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<div class="transparent-900 editSavedCodes m-auto my-20 hidden w-4/5 rounded-2xl p-10 text-left"> | ||
<h1 class="px-10">Edit saved codes</h1> | ||
<div class="mx-auto flex flex-col items-center justify-center rounded-2xl p-10"> | ||
<div class="transparent-800 mb-5 flex w-full flex-row items-center justify-between rounded-xl p-5 text-left"> | ||
<div> | ||
<h2>Save changes</h2> | ||
<h3>Save the changes you made. This will overwrite your codes.</h3> | ||
</div> | ||
<div class="ml-10 flex gap-3"> | ||
<button class="button" on:click={saveChanges}> | ||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> | ||
<path stroke="none" d="M0 0h24v24H0z" fill="none" /> | ||
<path d="M6 4h10l4 4v10a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2" /> | ||
<circle cx="12" cy="14" r="2" /> | ||
<polyline points="14 4 14 8 8 8 8 4" /> | ||
</svg> | ||
Save changes | ||
</button> | ||
</div> | ||
</div> | ||
|
||
<div class="transparent-800 mb-5 flex w-full flex-row items-center justify-between rounded-xl p-5 text-left"> | ||
<div> | ||
<h2>Revert changes</h2> | ||
<h3>You can revert the current changes you made.</h3> | ||
</div> | ||
<div class="ml-10 flex gap-3"> | ||
<button class="button"> | ||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> | ||
<path stroke="none" d="M0 0h24v24H0z" fill="none" /> | ||
<path d="M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4" /> | ||
<path d="M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4" /> | ||
<line x1="12" y1="9" x2="12" y2="12" /> | ||
<line x1="12" y1="15" x2="12.01" y2="15" /> | ||
</svg> | ||
Revert changes | ||
</button> | ||
</div> | ||
</div> | ||
|
||
<div class="transparent-800 mb-5 flex w-full flex-row items-center justify-between rounded-xl p-5 text-left"> | ||
<div> | ||
<h2>Delete codes</h2> | ||
<h3>You can delete all codes. Be careful, this can not be undone.</h3> | ||
</div> | ||
<div class="ml-10 flex gap-3"> | ||
<button class="button" on:click={deleteCodes}> | ||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | ||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /> | ||
</svg> | ||
Delete codes | ||
</button> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<div class="transparent-900 loadedCodes m-auto mb-20 hidden w-4/5 rounded-2xl p-10 text-left"> | ||
<h1 class="px-10">Loaded codes</h1> | ||
<div class="content mx-auto flex flex-col items-center justify-center rounded-2xl p-10" /> | ||
</div> | ||
|
||
<script lang="ts"> | ||
import { deleteCodes, loadSavedCodes, saveChanges } from "./index" | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
import { textConverter } from "../../libraries/convert" | ||
import { encrypt, decrypt, generateMasterKey } from "../../libraries/auth" | ||
import { dialog, fs, path } from "@tauri-apps/api" | ||
import { getSettings, setSettings } from "../../stores/settings" | ||
import { generateTimestamp } from "../../libraries/time" | ||
import { getState, setState } from "../../stores/state" | ||
import { navigate } from "../../libraries/navigate" | ||
|
||
let names: string[] = [] | ||
let issuers: string[] = [] | ||
let secrets: string[] = [] | ||
|
||
const generateEditElements = () => { | ||
document.querySelector(".editSavedCodes").style.display = "block" | ||
document.querySelector(".loadedCodes").style.display = "block" | ||
document.querySelector(".loadSavedCodes").style.display = "none" | ||
|
||
for (let i = 0; i < names.length; i++) { | ||
// create div | ||
const element = document.createElement("div") | ||
|
||
// set div content | ||
element.innerHTML = ` | ||
<div class="flex flex-wrap gap-5"> | ||
<div> | ||
<h5>Name</h5> | ||
<input id="issuer${i}" class="input mt-1" type="text" value="${issuers[i]}" readonly /> | ||
</div> | ||
<div> | ||
<h5>Description</h5> | ||
<input id="name${i}" class="input mt-1 w-96" type="text" value="${names[i]}" readonly /> | ||
</div> | ||
</div> | ||
<div class="ml-10 flex gap-3"> | ||
<button id="editCode${i}" class="button requirePassword"> | ||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"> | ||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" /> | ||
</svg> | ||
Edit | ||
</button> | ||
<button id="deleteCode${i}" class="button requirePassword"> | ||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"> | ||
<path stroke-linecap="round" stroke-linejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /> | ||
</svg> | ||
Delete | ||
</button> | ||
</div>` | ||
|
||
// add div | ||
element.classList.add("edit") | ||
element.setAttribute("id", `edit${i}`) | ||
|
||
document.querySelector(".content").appendChild(element) | ||
|
||
document.querySelector(`#editCode${i}`).addEventListener("click", () => { | ||
editCode(i) | ||
}) | ||
|
||
document.querySelector(`#deleteCode${i}`).addEventListener("click", () => { | ||
deleteCode(i) | ||
}) | ||
} | ||
} | ||
|
||
export const loadSavedCodes = async () => { | ||
const settings = getSettings() | ||
const filePath = await path.join(await path.configDir(), "Levminer", "Authme 4", "codes", "codes.authme") | ||
|
||
let file: LibAuthmeFile | ||
|
||
try { | ||
file = JSON.parse(await fs.readTextFile(filePath)) | ||
} catch (error) { | ||
return dialog.message("No save file found. \n\nGo to the codes or the import page and import your codes!", { type: "error" }) | ||
} | ||
|
||
const password = Buffer.from(settings.security.password, "base64") | ||
const key = Buffer.from(settings.security.key, "base64") | ||
|
||
const masterKey = await generateMasterKey(password, key) | ||
|
||
const decrypted = await decrypt(file.codes, masterKey) | ||
|
||
const data = textConverter(decrypted, 0) | ||
|
||
names = data.names | ||
issuers = data.issuers | ||
secrets = data.secrets | ||
|
||
generateEditElements() | ||
} | ||
|
||
export const saveChanges = async () => { | ||
const settings = getSettings() | ||
|
||
let saveText = "" | ||
|
||
for (let i = 0; i < names.length; i++) { | ||
const string = `\nName: ${names[i]} \nSecret: ${secrets[i]} \nIssuer: ${issuers[i]} \nType: OTP_TOTP\n` | ||
saveText += string | ||
} | ||
|
||
const password = Buffer.from(settings.security.password, "base64") | ||
const key = Buffer.from(settings.security.key, "base64") | ||
|
||
const masterKey = await generateMasterKey(password, key) | ||
|
||
const encrypted = await encrypt(saveText, masterKey) | ||
|
||
const fileContents: LibAuthmeFile = { | ||
codes: encrypted, | ||
encrypted: true, | ||
version: 3, | ||
role: "codes", | ||
date: generateTimestamp(), | ||
} | ||
const filePath = await path.join(await path.configDir(), "Levminer", "Authme 4", "codes", "codes.authme") | ||
|
||
await fs.writeTextFile(filePath, JSON.stringify(fileContents, null, "\t")) | ||
} | ||
|
||
/** | ||
* Delete all imported codes | ||
*/ | ||
export const deleteCodes = async () => { | ||
const confirm0 = await dialog.ask("Are you sure you want to delete all codes? \n\nYou can not revert this.", { type: "warning" }) | ||
|
||
if (confirm0 === false) { | ||
return | ||
} | ||
|
||
const confirm1 = await dialog.ask("Are you absolutely sure? \n\nThere is no way back!", { type: "warning" }) | ||
|
||
if (confirm1 === true) { | ||
const filePath = await path.join(await path.configDir(), "Levminer", "Authme 4", "codes", "codes.authme") | ||
await fs.removeFile(filePath) | ||
|
||
navigate("/") | ||
} | ||
} | ||
|
||
export const editCode = (id: number) => { | ||
const issuer: HTMLInputElement = document.querySelector(`#issuer${id}`) | ||
const name: HTMLInputElement = document.querySelector(`#name${id}`) | ||
|
||
issuer.focus() | ||
const length = issuer.value.length | ||
issuer.setSelectionRange(length, length) | ||
|
||
if (issuer.readOnly === true) { | ||
issuer.readOnly = false | ||
name.readOnly = false | ||
} else { | ||
issuer.readOnly = true | ||
name.readOnly = true | ||
|
||
const newIssuer = document.querySelector(`#issuer${id}`).value | ||
const newName = document.querySelector(`#name${id}`).value | ||
|
||
issuers[id] = newIssuer | ||
names[id] = newName | ||
} | ||
} | ||
|
||
export const deleteCode = async (id: number) => { | ||
const res = await dialog.ask("Are you sure?") | ||
|
||
if (res === true) { | ||
names.splice(id, 1) | ||
secrets.splice(id, 1) | ||
issuers.splice(id, 1) | ||
|
||
document.querySelector(`#edit${id}`).remove() | ||
} | ||
} |