forked from jsnbuchanan/crowd-funder-for-time-pwa
- Add configurable entity display logic via function props to EntityGrid - Implement comprehensive test suite for EntityGrid function props in TestView - Apply consistent code formatting across 15 components and views - Fix linting issues with trailing commas and line breaks - Add new EntityGridFunctionPropTest.vue for component testing - Update endorserServer with improved error handling and logging - Streamline PlatformServiceMixin with better cache management - Enhance component documentation and type safety Changes span 15 files with 159 additions and 69 deletions, focusing on component flexibility, code quality, and testing infrastructure.
790 lines
23 KiB
Vue
790 lines
23 KiB
Vue
<template>
|
|
<QuickNav />
|
|
|
|
<!-- CONTENT -->
|
|
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
|
|
<!-- Breadcrumb -->
|
|
<div class="mb-8">
|
|
<!-- Back -->
|
|
<div class="text-lg text-center font-light relative px-7">
|
|
<h1
|
|
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
|
|
@click="$router.back()"
|
|
>
|
|
<font-awesome icon="chevron-left" class="fa-fw"></font-awesome>
|
|
</h1>
|
|
</div>
|
|
|
|
<!-- Heading -->
|
|
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8">
|
|
Test
|
|
</h1>
|
|
</div>
|
|
|
|
<div>
|
|
<h2 class="text-xl font-bold mb-4">Notiwind Alerts</h2>
|
|
|
|
<!-- Notification test buttons using computed configuration -->
|
|
<button
|
|
v-for="config in notificationTestButtons"
|
|
:key="config.label"
|
|
:class="config.classes"
|
|
@click="triggerTestNotification(config)"
|
|
>
|
|
{{ config.label }}
|
|
</button>
|
|
</div>
|
|
|
|
<div class="mt-8">
|
|
<h2 class="text-xl font-bold mb-4">SQL Operations</h2>
|
|
<div class="flex gap-2 mt-2">
|
|
<button :class="sqlLinkClasses" @click="setAllTablesQuery">
|
|
All Tables
|
|
</button>
|
|
<button :class="sqlLinkClasses" @click="setAccountsQuery">
|
|
Accounts
|
|
</button>
|
|
<button :class="sqlLinkClasses" @click="setContactsQuery">
|
|
Contacts
|
|
</button>
|
|
<button :class="sqlLinkClasses" @click="setSettingsQuery">
|
|
Settings
|
|
</button>
|
|
</div>
|
|
<div>
|
|
<textarea
|
|
v-model="sqlQuery"
|
|
class="w-full h-32 p-2 border border-gray-300 rounded-md font-mono"
|
|
placeholder="Enter your SQL query here..."
|
|
></textarea>
|
|
</div>
|
|
<div class="mt-4">
|
|
<button :class="primaryButtonClasses" @click="executeSql">
|
|
Execute
|
|
</button>
|
|
</div>
|
|
<div v-if="sqlResult" class="mt-4">
|
|
<h3 class="text-lg font-semibold mb-2">Result:</h3>
|
|
<pre class="bg-gray-100 p-4 rounded-md overflow-x-auto">{{
|
|
JSON.stringify(sqlResult, null, 2)
|
|
}}</pre>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-8">
|
|
<h2 class="text-xl font-bold mb-4">Image Sharing</h2>
|
|
Populates the "shared-photo" view as if they used "share_target".
|
|
<input type="file" data-testId="fileInput" @change="uploadFile" />
|
|
<router-link
|
|
v-if="showFileNextStep()"
|
|
:to="{
|
|
name: 'shared-photo',
|
|
query: { fileName },
|
|
}"
|
|
class="block w-full text-center text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mb-2 mt-2"
|
|
data-testId="fileUploadButton"
|
|
>
|
|
Go to Shared Page
|
|
</router-link>
|
|
</div>
|
|
|
|
<div class="mt-8">
|
|
<h2 class="text-xl font-bold mb-4">Passkeys</h2>
|
|
See console for results.
|
|
<br />
|
|
See existing passkeys in Chrome at: chrome://settings/passkeys
|
|
<br />
|
|
Active DID: {{ activeDIDDisplay }}
|
|
{{ passkeyStatusDisplay }}
|
|
|
|
<div>
|
|
Register Passkey
|
|
<button :class="primaryButtonClasses" @click="register()">
|
|
Simplewebauthn
|
|
</button>
|
|
</div>
|
|
|
|
<div>
|
|
Create JWT
|
|
<button
|
|
:class="primaryButtonClasses"
|
|
@click="createJwtSimplewebauthn()"
|
|
>
|
|
Simplewebauthn
|
|
</button>
|
|
<button :class="primaryButtonClasses" @click="createJwtNavigator()">
|
|
Navigator
|
|
</button>
|
|
</div>
|
|
|
|
<div v-if="jwt">
|
|
Verify New JWT
|
|
<button :class="primaryButtonClasses" @click="verifySimplewebauthn()">
|
|
Simplewebauthn
|
|
</button>
|
|
<button :class="primaryButtonClasses" @click="verifyWebCrypto()">
|
|
WebCrypto
|
|
</button>
|
|
<button :class="primaryButtonClasses" @click="verifyP256()">
|
|
p256 - broken
|
|
</button>
|
|
</div>
|
|
<div v-else>Verify New JWT -- requires creation first</div>
|
|
<button :class="primaryButtonClasses" @click="verifyMyJwt()">
|
|
Verify Hard-Coded JWT
|
|
</button>
|
|
</div>
|
|
|
|
<div class="mt-8">
|
|
<h2 class="text-xl font-bold mb-4">Encryption & Decryption</h2>
|
|
See console for more output.
|
|
<div>
|
|
<button
|
|
:class="primaryButtonClasses"
|
|
@click="testMessageEncryptionDecryption()"
|
|
>
|
|
Run Test for Message Encryption/Decryption
|
|
</button>
|
|
{{ encryptionTestResultDisplay }}
|
|
</div>
|
|
<div>
|
|
<button
|
|
:class="primaryButtonClasses"
|
|
@click="testSimpleEncryptionDecryption()"
|
|
>
|
|
Run Test for Simple Encryption/Decryption
|
|
</button>
|
|
{{ simpleEncryptionTestResultDisplay }}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-8">
|
|
<h2 class="text-xl font-bold mb-4">Component Tests</h2>
|
|
Interactive tests for Vue components and their functionality.
|
|
|
|
<div class="mt-4">
|
|
<h3 class="text-lg font-semibold mb-2">EntityGrid Function Props</h3>
|
|
<p class="text-sm text-gray-600 mb-3">
|
|
Test the new function prop functionality in EntityGrid component.
|
|
</p>
|
|
<button
|
|
:class="primaryButtonClasses"
|
|
@click="showEntityGridTest = !showEntityGridTest"
|
|
>
|
|
{{ showEntityGridTest ? "Hide" : "Show" }} EntityGrid Function Prop
|
|
Test
|
|
</button>
|
|
|
|
<div
|
|
v-if="showEntityGridTest"
|
|
class="mt-4 p-4 border border-gray-300 rounded-md bg-gray-50"
|
|
>
|
|
<EntityGridFunctionPropTest />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-4">
|
|
<h3 class="text-lg font-semibold mb-2">Platform Service Mixin</h3>
|
|
<p class="text-sm text-gray-600 mb-3">
|
|
Test database operations through PlatformServiceMixin.
|
|
</p>
|
|
<button
|
|
:class="primaryButtonClasses"
|
|
@click="showPlatformServiceTest = !showPlatformServiceTest"
|
|
>
|
|
{{ showPlatformServiceTest ? "Hide" : "Show" }} Platform Service Test
|
|
</button>
|
|
|
|
<div
|
|
v-if="showPlatformServiceTest"
|
|
class="mt-4 p-4 border border-gray-300 rounded-md bg-gray-50"
|
|
>
|
|
<PlatformServiceMixinTest />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { Buffer } from "buffer/";
|
|
import { Base64URLString } from "@simplewebauthn/types";
|
|
import { ref } from "vue";
|
|
import { Component, Vue } from "vue-facing-decorator";
|
|
import { Router } from "vue-router";
|
|
|
|
import QuickNav from "../components/QuickNav.vue";
|
|
import { AppString, NotificationIface } from "../constants/app";
|
|
import {
|
|
NOTIFY_SQL_ERROR,
|
|
createSqlErrorMessage,
|
|
createPasskeyNameModal,
|
|
} from "../constants/notifications";
|
|
import * as vcLib from "../libs/crypto/vc";
|
|
import * as cryptoLib from "../libs/crypto";
|
|
|
|
import {
|
|
PeerSetup,
|
|
verifyJwtP256,
|
|
verifyJwtSimplewebauthn,
|
|
verifyJwtWebCrypto,
|
|
} from "../libs/crypto/vc/passkeyDidPeer";
|
|
import {
|
|
blobToBase64,
|
|
retrieveAccountMetadata,
|
|
registerAndSavePasskey,
|
|
SHARED_PHOTO_BASE64_KEY,
|
|
} from "../libs/util";
|
|
import { logger } from "../utils/logger";
|
|
import { Account } from "../db/tables/accounts";
|
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
|
import EntityGridFunctionPropTest from "../test/EntityGridFunctionPropTest.vue";
|
|
import PlatformServiceMixinTest from "../test/PlatformServiceMixinTest.vue";
|
|
|
|
const inputFileNameRef = ref<Blob>();
|
|
|
|
const TEST_PAYLOAD = {
|
|
vc: {
|
|
credentialSubject: {
|
|
"@context": "https://schema.org",
|
|
"@type": "GiveAction",
|
|
description: "pizza",
|
|
},
|
|
},
|
|
};
|
|
|
|
/**
|
|
* TestView Component
|
|
*
|
|
* Development/testing interface providing comprehensive testing tools for:
|
|
* - Notification system testing (8 different types)
|
|
* - Interactive SQL operations and database queries
|
|
* - File upload and image sharing functionality
|
|
* - Passkey registration and JWT verification
|
|
* - Encryption/decryption testing
|
|
* - Various crypto operations
|
|
*
|
|
* Features:
|
|
* - Raw SQL query execution interface for database testing
|
|
* - Notification type demonstrations
|
|
* - Passkey and JWT verification workflows
|
|
* - File upload with temporary storage
|
|
* - Crypto library testing utilities
|
|
*
|
|
* Security Considerations:
|
|
* - Test environment only - not for production use
|
|
* - SQL operations are intentionally raw for testing purposes
|
|
* - File uploads stored temporarily for testing workflows
|
|
*
|
|
* @author Matthew Raymer
|
|
*/
|
|
@Component({
|
|
components: {
|
|
QuickNav,
|
|
EntityGridFunctionPropTest,
|
|
PlatformServiceMixinTest,
|
|
},
|
|
mixins: [PlatformServiceMixin],
|
|
})
|
|
export default class Help extends Vue {
|
|
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
|
$router!: Router;
|
|
|
|
// for encryption/decryption
|
|
messageEncryptionTestResult?: boolean;
|
|
simpleEncryptionTestResult?: boolean;
|
|
|
|
// for file import
|
|
fileName?: string;
|
|
|
|
// for passkeys
|
|
credIdHex?: string;
|
|
activeDid?: string;
|
|
jwt?: string;
|
|
peerSetup?: PeerSetup;
|
|
userName?: string;
|
|
|
|
// for SQL operations
|
|
sqlQuery = "";
|
|
sqlResult: unknown = null;
|
|
|
|
cryptoLib = cryptoLib;
|
|
|
|
// for component tests
|
|
showEntityGridTest = false;
|
|
showPlatformServiceTest = false;
|
|
|
|
/**
|
|
* Computed properties for template streamlining
|
|
* Eliminates repeated classes and logic in template
|
|
*/
|
|
|
|
/**
|
|
* Standard button class for primary actions
|
|
*/
|
|
get primaryButtonClasses(): string {
|
|
return "font-bold capitalize bg-slate-500 text-white px-3 py-2 rounded-md mr-2";
|
|
}
|
|
|
|
/**
|
|
* Dark button class for primary test actions
|
|
*/
|
|
get darkButtonClasses(): string {
|
|
return "font-bold capitalize bg-slate-900 text-white px-3 py-2 rounded-md mr-2";
|
|
}
|
|
|
|
/**
|
|
* Secondary button class for secondary test actions
|
|
*/
|
|
get secondaryButtonClasses(): string {
|
|
return "font-bold capitalize bg-slate-600 text-white px-3 py-2 rounded-md mr-2";
|
|
}
|
|
|
|
/**
|
|
* Success button class for success notifications
|
|
*/
|
|
get successButtonClasses(): string {
|
|
return "font-bold capitalize bg-emerald-600 text-white px-3 py-2 rounded-md mr-2";
|
|
}
|
|
|
|
/**
|
|
* Warning button class for warning notifications
|
|
*/
|
|
get warningButtonClasses(): string {
|
|
return "font-bold capitalize bg-amber-600 text-white px-3 py-2 rounded-md mr-2";
|
|
}
|
|
|
|
/**
|
|
* Danger button class for danger notifications
|
|
*/
|
|
get dangerButtonClasses(): string {
|
|
return "font-bold capitalize bg-rose-600 text-white px-3 py-2 rounded-md mr-2";
|
|
}
|
|
|
|
/**
|
|
* SQL link button class for inline SQL query buttons
|
|
*/
|
|
get sqlLinkClasses(): string {
|
|
return "text-sm text-blue-600 hover:text-blue-800 underline";
|
|
}
|
|
|
|
/**
|
|
* Formatted display of active DID status
|
|
*/
|
|
get activeDIDDisplay(): string {
|
|
return this.activeDid || "nothing, which";
|
|
}
|
|
|
|
/**
|
|
* Formatted display of passkey status
|
|
*/
|
|
get passkeyStatusDisplay(): string {
|
|
return this.credIdHex ? "has a passkey ID" : "has no passkey ID";
|
|
}
|
|
|
|
/**
|
|
* Formatted display of encryption test result
|
|
*/
|
|
get encryptionTestResultDisplay(): string {
|
|
return this.messageEncryptionTestResult !== undefined
|
|
? `Result: ${this.messageEncryptionTestResult}`
|
|
: "Result: Not tested";
|
|
}
|
|
|
|
/**
|
|
* Formatted display of simple encryption test result
|
|
*/
|
|
get simpleEncryptionTestResultDisplay(): string {
|
|
return this.simpleEncryptionTestResult !== undefined
|
|
? `Result: ${this.simpleEncryptionTestResult}`
|
|
: "Result: Not tested";
|
|
}
|
|
|
|
/**
|
|
* SQL query presets for template buttons
|
|
* Extracts inline SQL assignments from template for better organization
|
|
*/
|
|
setAllTablesQuery() {
|
|
this.sqlQuery = "SELECT * FROM sqlite_master WHERE type='table';";
|
|
this.executeSql();
|
|
}
|
|
|
|
setAccountsQuery() {
|
|
this.sqlQuery = "SELECT * FROM accounts;";
|
|
this.executeSql();
|
|
}
|
|
|
|
setContactsQuery() {
|
|
this.sqlQuery = "SELECT * FROM contacts;";
|
|
this.executeSql();
|
|
}
|
|
|
|
setSettingsQuery() {
|
|
this.sqlQuery = "SELECT * FROM settings;";
|
|
this.executeSql();
|
|
}
|
|
|
|
/**
|
|
* Configuration for notification test buttons
|
|
* Eliminates repetitive notification button definitions in template
|
|
*/
|
|
get notificationTestButtons() {
|
|
return [
|
|
{
|
|
label: "Toast",
|
|
classes: this.darkButtonClasses,
|
|
notification: {
|
|
group: "alert",
|
|
type: "toast",
|
|
title: "Toast",
|
|
text: "I'm a toast. Without a timeout, I'm stuck.",
|
|
},
|
|
timeout: 5000,
|
|
},
|
|
{
|
|
label: "Info",
|
|
classes: this.secondaryButtonClasses,
|
|
notification: {
|
|
group: "alert",
|
|
type: "info",
|
|
title: "Information Alert",
|
|
text: "Just wanted you to know.",
|
|
},
|
|
timeout: 5000,
|
|
},
|
|
{
|
|
label: "Success",
|
|
classes: this.successButtonClasses,
|
|
notification: {
|
|
group: "alert",
|
|
type: "success",
|
|
title: "Success Alert",
|
|
text: "Congratulations!",
|
|
},
|
|
timeout: 5000,
|
|
},
|
|
{
|
|
label: "Warning",
|
|
classes: this.warningButtonClasses,
|
|
notification: {
|
|
group: "alert",
|
|
type: "warning",
|
|
title: "Warning Alert",
|
|
text: "You might wanna look at this.",
|
|
},
|
|
timeout: 5000,
|
|
},
|
|
{
|
|
label: "Danger",
|
|
classes: this.dangerButtonClasses,
|
|
notification: {
|
|
group: "alert",
|
|
type: "danger",
|
|
title: "Danger Alert",
|
|
text: "Something terrible has happened!",
|
|
},
|
|
timeout: 5000,
|
|
},
|
|
{
|
|
label: "Notif ON",
|
|
classes: this.secondaryButtonClasses,
|
|
notification: {
|
|
group: "modal",
|
|
type: "notification-permission",
|
|
title: "Notification Permission",
|
|
text: "Enable notifications?",
|
|
},
|
|
timeout: -1,
|
|
},
|
|
{
|
|
label: "Notif MUTE",
|
|
classes: this.secondaryButtonClasses,
|
|
notification: {
|
|
group: "modal",
|
|
type: "notification-mute",
|
|
title: "Notification Settings",
|
|
text: "Notifications muted",
|
|
},
|
|
timeout: -1,
|
|
},
|
|
{
|
|
label: "Notif OFF",
|
|
classes: this.secondaryButtonClasses,
|
|
notification: {
|
|
group: "modal",
|
|
type: "notification-off",
|
|
title: "Notifications",
|
|
text: "Notifications turned off",
|
|
},
|
|
timeout: -1,
|
|
},
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Method to trigger notification test
|
|
* Centralizes notification testing logic
|
|
*/
|
|
triggerTestNotification(config: {
|
|
notification: NotificationIface;
|
|
timeout?: number;
|
|
}) {
|
|
this.$notify(config.notification, config.timeout);
|
|
}
|
|
|
|
/**
|
|
* Component initialization
|
|
*
|
|
* Loads user settings and account information for testing interface
|
|
* Uses PlatformServiceMixin for database access
|
|
*/
|
|
async mounted() {
|
|
const settings = await this.$accountSettings();
|
|
this.activeDid = settings.activeDid || "";
|
|
this.userName = settings.firstName;
|
|
|
|
const account = await retrieveAccountMetadata(this.activeDid);
|
|
if (this.activeDid) {
|
|
if (account) {
|
|
this.credIdHex = account.passkeyCredIdHex as string;
|
|
} else {
|
|
alert("No account found for DID " + this.activeDid);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles file upload for image sharing tests
|
|
*
|
|
* Processes uploaded files and stores them in temp table for shared photo testing
|
|
* Uses PlatformServiceMixin service methods for temp table operations
|
|
*/
|
|
async uploadFile(event: Event) {
|
|
const target = event.target as HTMLInputElement;
|
|
inputFileNameRef.value = target.files?.[0];
|
|
// https://developer.mozilla.org/en-US/docs/Web/API/File
|
|
// ... plus it has a `type` property from my testing
|
|
const file = inputFileNameRef.value;
|
|
if (file != null) {
|
|
const reader = new FileReader();
|
|
reader.onload = async (e) => {
|
|
const data = e.target?.result as ArrayBuffer;
|
|
if (data) {
|
|
const blob = new Blob([new Uint8Array(data)], {
|
|
type: file.type,
|
|
});
|
|
const blobB64 = await blobToBase64(blob);
|
|
this.fileName = (file as File).name;
|
|
|
|
// Use service methods for temp table operations
|
|
const temp = await this.$getTemp(SHARED_PHOTO_BASE64_KEY);
|
|
if (temp) {
|
|
await this.$updateEntity("temp", { blobB64 }, "id = ?", [
|
|
SHARED_PHOTO_BASE64_KEY,
|
|
]);
|
|
} else {
|
|
await this.$insertEntity(
|
|
"temp",
|
|
{ id: SHARED_PHOTO_BASE64_KEY, blobB64 },
|
|
["id", "blobB64"],
|
|
);
|
|
}
|
|
}
|
|
};
|
|
reader.readAsArrayBuffer(file as Blob);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if file upload next step should be shown
|
|
*/
|
|
showFileNextStep() {
|
|
return !!inputFileNameRef.value;
|
|
}
|
|
|
|
/**
|
|
* Handles passkey registration for testing
|
|
*
|
|
* Creates new passkey with user name or default test name
|
|
* Includes validation and user confirmation workflow
|
|
* Uses notification helpers for consistent messaging
|
|
*/
|
|
public async register() {
|
|
const DEFAULT_USERNAME = AppString.APP_NAME + " Tester";
|
|
if (!this.userName) {
|
|
const modalConfig = createPasskeyNameModal(
|
|
DEFAULT_USERNAME,
|
|
async () => {
|
|
this.userName = DEFAULT_USERNAME;
|
|
},
|
|
async () => {
|
|
this.$router.push({ name: "new-edit-account" });
|
|
},
|
|
);
|
|
|
|
this.$notify(modalConfig, -1);
|
|
return;
|
|
}
|
|
const account = await registerAndSavePasskey(
|
|
AppString.APP_NAME + " - " + this.userName,
|
|
);
|
|
this.activeDid = account.did;
|
|
this.credIdHex = account.passkeyCredIdHex;
|
|
}
|
|
|
|
/**
|
|
* Tests message encryption/decryption functionality
|
|
*/
|
|
public async testMessageEncryptionDecryption() {
|
|
this.messageEncryptionTestResult =
|
|
await cryptoLib.testMessageEncryptionDecryption();
|
|
}
|
|
|
|
/**
|
|
* Tests simple encryption/decryption functionality
|
|
*/
|
|
public async testSimpleEncryptionDecryption() {
|
|
this.simpleEncryptionTestResult =
|
|
await cryptoLib.testSimpleEncryptionDecryption();
|
|
}
|
|
|
|
/**
|
|
* Creates JWT using SimpleWebAuthn for testing
|
|
*/
|
|
public async createJwtSimplewebauthn() {
|
|
const account: Account | undefined = await retrieveAccountMetadata(
|
|
this.activeDid || "",
|
|
);
|
|
if (!vcLib.isFromPasskey(account)) {
|
|
alert(`The DID ${this.activeDid} is not passkey-enabled.`);
|
|
return;
|
|
}
|
|
this.peerSetup = new PeerSetup();
|
|
this.jwt = await this.peerSetup.createJwtSimplewebauthn(
|
|
this.activeDid as string,
|
|
TEST_PAYLOAD,
|
|
this.credIdHex as string,
|
|
);
|
|
logger.log("simple jwt4url", this.jwt);
|
|
}
|
|
|
|
/**
|
|
* Creates JWT using Navigator API for testing
|
|
*/
|
|
public async createJwtNavigator() {
|
|
const account: Account | undefined = await retrieveAccountMetadata(
|
|
this.activeDid || "",
|
|
);
|
|
if (!vcLib.isFromPasskey(account)) {
|
|
alert(`The DID ${this.activeDid} is not passkey-enabled.`);
|
|
return;
|
|
}
|
|
this.peerSetup = new PeerSetup();
|
|
this.jwt = await this.peerSetup.createJwtNavigator(
|
|
this.activeDid as string,
|
|
TEST_PAYLOAD,
|
|
this.credIdHex as string,
|
|
);
|
|
logger.log("lower jwt4url", this.jwt);
|
|
}
|
|
|
|
/**
|
|
* Verifies JWT using P256 algorithm for testing
|
|
*/
|
|
public async verifyP256() {
|
|
const decoded = await verifyJwtP256(
|
|
this.activeDid as string,
|
|
this.peerSetup?.authenticatorData as ArrayBuffer,
|
|
this.peerSetup?.challenge as Uint8Array,
|
|
this.peerSetup?.signature as Base64URLString,
|
|
);
|
|
logger.log("decoded", decoded);
|
|
}
|
|
|
|
/**
|
|
* Verifies JWT using SimpleWebAuthn for testing
|
|
*/
|
|
public async verifySimplewebauthn() {
|
|
const decoded = await verifyJwtSimplewebauthn(
|
|
this.credIdHex as string,
|
|
this.activeDid as string,
|
|
this.peerSetup?.authenticatorData as ArrayBuffer,
|
|
this.peerSetup?.challenge as Uint8Array,
|
|
this.peerSetup?.clientDataJsonBase64Url as Base64URLString,
|
|
this.peerSetup?.signature as Base64URLString,
|
|
);
|
|
logger.log("decoded", decoded);
|
|
}
|
|
|
|
/**
|
|
* Verifies JWT using WebCrypto for testing
|
|
*/
|
|
public async verifyWebCrypto() {
|
|
const decoded = await verifyJwtWebCrypto(
|
|
this.activeDid as string,
|
|
this.peerSetup?.authenticatorData as ArrayBuffer,
|
|
this.peerSetup?.challenge as Uint8Array,
|
|
this.peerSetup?.signature as Base64URLString,
|
|
);
|
|
logger.log("decoded", decoded);
|
|
}
|
|
|
|
/**
|
|
* Verifies hard-coded JWT for testing purposes
|
|
*/
|
|
public async verifyMyJwt() {
|
|
const did =
|
|
"did:peer:0zKMFjvUgYrM1hXwDciYHiA9MxXtJPXnRLJvqoMNAKoDLX9pKMWLb3VDsgua1p2zW1xXRsjZSTNsfvMnNyMS7dB4k7NAhFwL3pXBrBXgyYJ9ri";
|
|
const jwt =
|
|
"eyJ0eXAiOiJKV0FOVCIsImFsZyI6IkVTMjU2In0.eyJBdXRoZW50aWNhdGlvbkRhdGFCNjRVUkwiOiJTWllONVlnT2pHaDBOQmNQWkhaZ1c0X2tycm1paGpMSG1Wenp1b01kbDJNRkFBQUFBQSIsIkNsaWVudERhdGFKU09OQjY0VVJMIjoiZXlKMGVYQmxJam9pZDJWaVlYVjBhRzR1WjJWMElpd2lZMmhoYkd4bGJtZGxJam9pWlhsS01sbDVTVFpsZVVwcVkyMVdhMXBYTlRCaFYwWnpWVE5XYVdGdFZtcGtRMGsyWlhsS1FWa3lPWFZrUjFZMFpFTkpOa2x0YURCa1NFSjZUMms0ZG1NeVRtOWFWekZvVEcwNWVWcDVTWE5KYTBJd1pWaENiRWxxYjJsU01td3lXbFZHYW1SSGJIWmlhVWx6U1cxU2JHTXlUbmxoV0VJd1lWYzVkVWxxYjJsalIydzJaVzFGYVdaWU1ITkpiV3hvWkVOSk5rMVVZM2hQUkZVMFRtcHJOVTFEZDJsaFdFNTZTV3B2YVZwSGJHdFBia0pzV2xoSk5rMUljRXhVVlZweFpHeFdibGRZU2s1TlYyaFpaREJTYW1GV2JFbGhWVVUxVkZob1dXUkZjRkZYUnpWVFZFVndNbU5YT1U1VWEwWk1ZakJTVFZkRWJIZFRNREZZVkVkSmVsWnJVbnBhTTFab1RWaEJlV1ZzWTNobFJtaFRZekp3WVZVeFVrOWpNbG95VkZjMVQyVlZNVlJPTWxKRFRrZHpNMVJyUm05U2JtUk5UVE5DV1ZGdVNrTlhSMlExVjFWdk5XTnRhMmxtVVNJc0ltOXlhV2RwYmlJNkltaDBkSEE2THk5c2IyTmhiR2h2YzNRNk9EQTRNQ0lzSW1OeWIzTnpUM0pwWjJsdUlqcG1ZV3h6WlgwIiwiaWF0IjoxNzE4NTg2OTkyLCJpc3MiOiJkaWQ6cGVlcjowektNRmp2VWdZck0xaFh3RGNpWUhpQTlNeFh0SlBYblJMSnZxb01OQUtvRExYOXBLTVdMYjNWRHNndWExcDJ6VzF4WFJzalpTVE5zZnZNbk55TVM3ZEI0azdOQWhGd0wzcFhCckJYZ3lZSjlyaSJ9.MEUCIQDJyCTbMPIFnuBoW3FYnlgtDEIHZ2OrkCEvqVnHU7kJDQIgVxjBjfW1TwQfcSOYwK8Z7AdCWGJlyxtLEsrnPif7caE";
|
|
const pieces = jwt.split(".");
|
|
const payload = JSON.parse(Buffer.from(pieces[1], "base64").toString());
|
|
const authData = Buffer.from(payload["AuthenticationDataB64URL"], "base64");
|
|
const clientJSON = Buffer.from(
|
|
payload["ClientDataJSONB64URL"],
|
|
"base64",
|
|
).toString();
|
|
const clientData = JSON.parse(clientJSON);
|
|
const challenge = clientData.challenge;
|
|
const signatureB64URL = pieces[2];
|
|
const decoded = await verifyJwtWebCrypto(
|
|
did,
|
|
authData,
|
|
challenge,
|
|
signatureB64URL,
|
|
);
|
|
logger.log("decoded", decoded);
|
|
}
|
|
|
|
/**
|
|
* Executes SQL queries for testing database operations
|
|
*
|
|
* Supports both SELECT queries (dbQuery) and other SQL commands (dbExec)
|
|
* Provides interface for testing raw SQL operations
|
|
* Uses PlatformServiceMixin for database access and notification helpers for errors
|
|
*/
|
|
async executeSql() {
|
|
try {
|
|
const isSelect = this.sqlQuery.trim().toLowerCase().startsWith("select");
|
|
if (isSelect) {
|
|
this.sqlResult = await this.$query(this.sqlQuery);
|
|
} else {
|
|
this.sqlResult = await this.$exec(this.sqlQuery);
|
|
}
|
|
logger.log("Test SQL Result:", this.sqlResult);
|
|
} catch (error) {
|
|
logger.error("Test SQL Error:", error);
|
|
this.$notify(
|
|
{
|
|
group: "alert",
|
|
type: "danger",
|
|
title: NOTIFY_SQL_ERROR.title,
|
|
text: createSqlErrorMessage(error),
|
|
},
|
|
5000,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
</script>
|