aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LICENSE2
-rw-r--r--back-end/libs/NVault.Crypto.Secp256k1/src/ContextExtensions.cs2
-rw-r--r--back-end/libs/NVault.Crypto.Secp256k1/src/IRandomSource.cs2
-rw-r--r--back-end/libs/NVault.Crypto.Secp256k1/src/LibSecp256k1.cs2
-rw-r--r--back-end/libs/NVault.Crypto.Secp256k1/src/NVault.Crypto.Secp256k1.csproj6
-rw-r--r--back-end/libs/NVault.Crypto.Secp256k1/src/Secp256HashFuncState.cs2
-rw-r--r--back-end/libs/NVault.Crypto.Secp256k1/src/Secp256k1Context.cs2
-rw-r--r--back-end/libs/NVault.Crypto.Secp256k1/src/Secp256k1SecretKey.cs2
-rw-r--r--back-end/libs/NVault.Crypto.Secp256k1/src/UnmanagedRandomSource.cs2
-rw-r--r--back-end/libs/NVault.VaultExtensions/src/IClientAccessScope.cs2
-rw-r--r--back-end/libs/NVault.VaultExtensions/src/IKvVaultStore.cs2
-rw-r--r--back-end/libs/NVault.VaultExtensions/src/IVaultClientScope.cs2
-rw-r--r--back-end/libs/NVault.VaultExtensions/src/IVaultKvClientScope.cs2
-rw-r--r--back-end/libs/NVault.VaultExtensions/src/KvVaultStorage.cs2
-rw-r--r--back-end/libs/NVault.VaultExtensions/src/NVault.VaultExtensions.csproj4
-rw-r--r--back-end/libs/NVault.VaultExtensions/src/VaultClientExtensions.cs2
-rw-r--r--back-end/libs/NVault.VaultExtensions/src/VaultUserScope.cs2
-rw-r--r--back-end/plugins/nvault/src/Base64KeyEncoder.cs2
-rw-r--r--back-end/plugins/nvault/src/EncryptionResult.cs2
-rw-r--r--back-end/plugins/nvault/src/Endpoints/Endpoint.cs18
-rw-r--r--back-end/plugins/nvault/src/INostrCryptoProvider.cs2
-rw-r--r--back-end/plugins/nvault/src/INostrKeyEncoder.cs2
-rw-r--r--back-end/plugins/nvault/src/INostrOperations.cs2
-rw-r--r--back-end/plugins/nvault/src/INostrVault.cs2
-rw-r--r--back-end/plugins/nvault/src/ManagedCryptoprovider.cs2
-rw-r--r--back-end/plugins/nvault/src/ManagedVaultClient.cs2
-rw-r--r--back-end/plugins/nvault/src/Model/NostrContext.cs2
-rw-r--r--back-end/plugins/nvault/src/Model/NostrEvent.cs2
-rw-r--r--back-end/plugins/nvault/src/Model/NostrKeyMeta.cs2
-rw-r--r--back-end/plugins/nvault/src/Model/NostrKeyMetaStore.cs2
-rw-r--r--back-end/plugins/nvault/src/Model/NostrRelay.cs2
-rw-r--r--back-end/plugins/nvault/src/Model/NostrRelayFlags.cs2
-rw-r--r--back-end/plugins/nvault/src/Model/NostrRelayStore.cs2
-rw-r--r--back-end/plugins/nvault/src/NVault.csproj16
-rw-r--r--back-end/plugins/nvault/src/NativeSecp256k1Library.cs2
-rw-r--r--back-end/plugins/nvault/src/NostrEntry.cs2
-rw-r--r--back-end/plugins/nvault/src/NostrMessageKind.cs2
-rw-r--r--back-end/plugins/nvault/src/NostrOpProvider.cs2
-rw-r--r--extension/src/entries/background/main.ts6
-rw-r--r--extension/src/entries/background/script.js2
-rw-r--r--extension/src/entries/background/serviceWorker.js2
-rw-r--r--extension/src/entries/contentScript/auth-popup.html10
-rw-r--r--extension/src/entries/contentScript/primary/components/PromptPopup.vue75
-rw-r--r--extension/src/entries/contentScript/primary/main.js5
-rw-r--r--extension/src/entries/contentScript/renderContent.js2
-rw-r--r--extension/src/entries/contentScript/util.ts38
-rw-r--r--extension/src/entries/nostr-provider.js2
-rw-r--r--extension/src/entries/options/App.vue26
-rw-r--r--extension/src/entries/options/components/AutoRules.vue119
-rw-r--r--extension/src/entries/options/components/EvHistoryTable.vue84
-rw-r--r--extension/src/entries/options/components/EventHistory.vue132
-rw-r--r--extension/src/entries/options/main.js9
-rw-r--r--extension/src/entries/popup/Components/PageContent.vue21
-rw-r--r--extension/src/entries/popup/main.js5
-rw-r--r--extension/src/entries/store/features.ts8
-rw-r--r--extension/src/entries/store/identity.ts2
-rw-r--r--extension/src/entries/store/index.ts3
-rw-r--r--extension/src/entries/store/mfaconfig.ts2
-rw-r--r--extension/src/entries/store/permissions.ts105
-rw-r--r--extension/src/features/framework/index.ts2
-rw-r--r--extension/src/features/history.ts2
-rw-r--r--extension/src/features/identity-api.ts2
-rw-r--r--extension/src/features/index.ts7
-rw-r--r--extension/src/features/mfa-api.ts2
-rw-r--r--extension/src/features/nip07allow-api.ts2
-rw-r--r--extension/src/features/nostr-api.ts2
-rw-r--r--extension/src/features/permissions.ts371
-rw-r--r--extension/src/features/pki-api.ts2
-rw-r--r--extension/src/features/server-api/endpoints.ts2
-rw-r--r--extension/src/features/server-api/index.ts2
-rw-r--r--extension/src/features/settings.ts2
-rw-r--r--extension/src/features/tagfilter-api.ts2
-rw-r--r--extension/src/features/types.ts2
-rw-r--r--extension/src/features/util.ts10
-rw-r--r--extension/src/manifest.js2
-rw-r--r--extension/vite.config.js3
76 files changed, 978 insertions, 209 deletions
diff --git a/LICENSE b/LICENSE
index a169586..6db2c01 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2023 Vaughn Nugent
+Copyright (C) 2024 Vaughn Nugent
Contact information
Name: Vaughn Nugent
diff --git a/back-end/libs/NVault.Crypto.Secp256k1/src/ContextExtensions.cs b/back-end/libs/NVault.Crypto.Secp256k1/src/ContextExtensions.cs
index 556bba7..bb014df 100644
--- a/back-end/libs/NVault.Crypto.Secp256k1/src/ContextExtensions.cs
+++ b/back-end/libs/NVault.Crypto.Secp256k1/src/ContextExtensions.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/libs/NVault.Crypto.Secp256k1/src/IRandomSource.cs b/back-end/libs/NVault.Crypto.Secp256k1/src/IRandomSource.cs
index 542fc9c..4e1861d 100644
--- a/back-end/libs/NVault.Crypto.Secp256k1/src/IRandomSource.cs
+++ b/back-end/libs/NVault.Crypto.Secp256k1/src/IRandomSource.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/libs/NVault.Crypto.Secp256k1/src/LibSecp256k1.cs b/back-end/libs/NVault.Crypto.Secp256k1/src/LibSecp256k1.cs
index 907eaa4..8dda269 100644
--- a/back-end/libs/NVault.Crypto.Secp256k1/src/LibSecp256k1.cs
+++ b/back-end/libs/NVault.Crypto.Secp256k1/src/LibSecp256k1.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/libs/NVault.Crypto.Secp256k1/src/NVault.Crypto.Secp256k1.csproj b/back-end/libs/NVault.Crypto.Secp256k1/src/NVault.Crypto.Secp256k1.csproj
index 59ff06e..5014d89 100644
--- a/back-end/libs/NVault.Crypto.Secp256k1/src/NVault.Crypto.Secp256k1.csproj
+++ b/back-end/libs/NVault.Crypto.Secp256k1/src/NVault.Crypto.Secp256k1.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net6.0</TargetFramework>
+ <TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PackageReadmeFile>README.md</PackageReadmeFile>
@@ -20,8 +20,8 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="VNLib.Hashing.Portable" Version="0.1.0-ci0107" />
- <PackageReference Include="VNLib.Utils" Version="0.1.0-ci0107" />
+ <PackageReference Include="VNLib.Hashing.Portable" Version="0.1.0-ci0109" />
+ <PackageReference Include="VNLib.Utils" Version="0.1.0-ci0109" />
</ItemGroup>
</Project>
diff --git a/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256HashFuncState.cs b/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256HashFuncState.cs
index 4ee745c..c82321c 100644
--- a/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256HashFuncState.cs
+++ b/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256HashFuncState.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256k1Context.cs b/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256k1Context.cs
index f0a795a..dfb3ff8 100644
--- a/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256k1Context.cs
+++ b/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256k1Context.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256k1SecretKey.cs b/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256k1SecretKey.cs
index 7224720..35734ae 100644
--- a/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256k1SecretKey.cs
+++ b/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256k1SecretKey.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/libs/NVault.Crypto.Secp256k1/src/UnmanagedRandomSource.cs b/back-end/libs/NVault.Crypto.Secp256k1/src/UnmanagedRandomSource.cs
index d6ff5aa..360de21 100644
--- a/back-end/libs/NVault.Crypto.Secp256k1/src/UnmanagedRandomSource.cs
+++ b/back-end/libs/NVault.Crypto.Secp256k1/src/UnmanagedRandomSource.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/libs/NVault.VaultExtensions/src/IClientAccessScope.cs b/back-end/libs/NVault.VaultExtensions/src/IClientAccessScope.cs
index 7a83fd7..c79f75e 100644
--- a/back-end/libs/NVault.VaultExtensions/src/IClientAccessScope.cs
+++ b/back-end/libs/NVault.VaultExtensions/src/IClientAccessScope.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/libs/NVault.VaultExtensions/src/IKvVaultStore.cs b/back-end/libs/NVault.VaultExtensions/src/IKvVaultStore.cs
index 261bd7c..037fe6c 100644
--- a/back-end/libs/NVault.VaultExtensions/src/IKvVaultStore.cs
+++ b/back-end/libs/NVault.VaultExtensions/src/IKvVaultStore.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/libs/NVault.VaultExtensions/src/IVaultClientScope.cs b/back-end/libs/NVault.VaultExtensions/src/IVaultClientScope.cs
index 873a115..d53bc4a 100644
--- a/back-end/libs/NVault.VaultExtensions/src/IVaultClientScope.cs
+++ b/back-end/libs/NVault.VaultExtensions/src/IVaultClientScope.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/libs/NVault.VaultExtensions/src/IVaultKvClientScope.cs b/back-end/libs/NVault.VaultExtensions/src/IVaultKvClientScope.cs
index 951f5e2..f763473 100644
--- a/back-end/libs/NVault.VaultExtensions/src/IVaultKvClientScope.cs
+++ b/back-end/libs/NVault.VaultExtensions/src/IVaultKvClientScope.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/libs/NVault.VaultExtensions/src/KvVaultStorage.cs b/back-end/libs/NVault.VaultExtensions/src/KvVaultStorage.cs
index b679404..8a2b9b6 100644
--- a/back-end/libs/NVault.VaultExtensions/src/KvVaultStorage.cs
+++ b/back-end/libs/NVault.VaultExtensions/src/KvVaultStorage.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/libs/NVault.VaultExtensions/src/NVault.VaultExtensions.csproj b/back-end/libs/NVault.VaultExtensions/src/NVault.VaultExtensions.csproj
index 3783c83..e5dbe8c 100644
--- a/back-end/libs/NVault.VaultExtensions/src/NVault.VaultExtensions.csproj
+++ b/back-end/libs/NVault.VaultExtensions/src/NVault.VaultExtensions.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net6.0</TargetFramework>
+ <TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PackageReadmeFile>README.md</PackageReadmeFile>
@@ -21,7 +21,7 @@
<ItemGroup>
<PackageReference Include="VaultSharp" Version="1.13.0.1" />
- <PackageReference Include="VNLib.Plugins.Extensions.Loading" Version="0.1.0-ci0045" />
+ <PackageReference Include="VNLib.Plugins.Extensions.Loading" Version="0.1.0-ci0047" />
</ItemGroup>
</Project>
diff --git a/back-end/libs/NVault.VaultExtensions/src/VaultClientExtensions.cs b/back-end/libs/NVault.VaultExtensions/src/VaultClientExtensions.cs
index 9ea9d24..d90941a 100644
--- a/back-end/libs/NVault.VaultExtensions/src/VaultClientExtensions.cs
+++ b/back-end/libs/NVault.VaultExtensions/src/VaultClientExtensions.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/libs/NVault.VaultExtensions/src/VaultUserScope.cs b/back-end/libs/NVault.VaultExtensions/src/VaultUserScope.cs
index b70028e..0e8796c 100644
--- a/back-end/libs/NVault.VaultExtensions/src/VaultUserScope.cs
+++ b/back-end/libs/NVault.VaultExtensions/src/VaultUserScope.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/plugins/nvault/src/Base64KeyEncoder.cs b/back-end/plugins/nvault/src/Base64KeyEncoder.cs
index 6e852ef..3b09a19 100644
--- a/back-end/plugins/nvault/src/Base64KeyEncoder.cs
+++ b/back-end/plugins/nvault/src/Base64KeyEncoder.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/plugins/nvault/src/EncryptionResult.cs b/back-end/plugins/nvault/src/EncryptionResult.cs
index ad08629..10741d2 100644
--- a/back-end/plugins/nvault/src/EncryptionResult.cs
+++ b/back-end/plugins/nvault/src/EncryptionResult.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/plugins/nvault/src/Endpoints/Endpoint.cs b/back-end/plugins/nvault/src/Endpoints/Endpoint.cs
index 4223a10..b8aa0c9 100644
--- a/back-end/plugins/nvault/src/Endpoints/Endpoint.cs
+++ b/back-end/plugins/nvault/src/Endpoints/Endpoint.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
@@ -27,6 +27,7 @@ using FluentValidation;
using NVault.VaultExtensions;
+using VNLib.Utils.Logging;
using VNLib.Utils.Extensions;
using VNLib.Plugins;
using VNLib.Plugins.Essentials;
@@ -46,6 +47,8 @@ namespace NVault.Plugins.Vault.Endpoints
[ConfigurationName("endpoint")]
internal class Endpoint : ProtectedWebEndpoint
{
+ const string EventLogTemplate = "Method {m}, UserID {uid}, Type {tp} Payload {p}";
+
private static IValidator<NostrEvent> EventValidator { get; } = NostrEvent.GetValidator();
private static IValidator<NostrRelay> RelayValidator { get; } = NostrRelay.GetValidator();
private static IValidator<NostrKeyMeta> KeyMetaValidator { get; } = NostrKeyMeta.GetValidator();
@@ -57,6 +60,7 @@ namespace NVault.Plugins.Vault.Endpoints
private readonly NostrRelayStore _relays;
private readonly NostrKeyMetaStore _publicKeyStore;
private readonly bool AllowDelete;
+ private readonly ILogProvider? _abnoxiousLog;
public Endpoint(PluginBase plugin, IConfigScope config)
{
@@ -71,6 +75,12 @@ namespace NVault.Plugins.Vault.Endpoints
_relays = new NostrRelayStore(options);
_publicKeyStore = new NostrKeyMetaStore(options);
_vault = new NostrOpProvider(plugin);
+
+ //Check for obnoxious logging
+ if (plugin.HostArgs.HasArgument("--nvault-obnoxious"))
+ {
+ _abnoxiousLog = plugin.Log.CreateScope("NVAULT EVENT");
+ }
}
@@ -151,6 +161,8 @@ namespace NVault.Plugins.Vault.Endpoints
return VirtualOk(entity, webm);
}
+ _abnoxiousLog?.Information(EventLogTemplate, "POST", entity.Session.UserID[..10], "sign-event", nEvent);
+
//Create user scope
VaultUserScope scope = new(entity.Session.UserID);
@@ -193,6 +205,8 @@ namespace NVault.Plugins.Vault.Endpoints
return VirtualClose(entity, webm, HttpStatusCode.NotFound);
}
+ _abnoxiousLog?.Information(EventLogTemplate, "POST", entity.Session.UserID[..10], "decrypt-message", request);
+
VaultUserScope scope = new(entity.Session.UserID);
//Try to decrypt the message
@@ -233,6 +247,8 @@ namespace NVault.Plugins.Vault.Endpoints
return VirtualClose(entity, webm, HttpStatusCode.NotFound);
}
+ _abnoxiousLog?.Information(EventLogTemplate, "POST", entity.Session.UserID[..10], "encrypt-message", request);
+
VaultUserScope scope = new(entity.Session.UserID);
try
{
diff --git a/back-end/plugins/nvault/src/INostrCryptoProvider.cs b/back-end/plugins/nvault/src/INostrCryptoProvider.cs
index b66757c..c65d3e9 100644
--- a/back-end/plugins/nvault/src/INostrCryptoProvider.cs
+++ b/back-end/plugins/nvault/src/INostrCryptoProvider.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/plugins/nvault/src/INostrKeyEncoder.cs b/back-end/plugins/nvault/src/INostrKeyEncoder.cs
index a76ab19..fe1b379 100644
--- a/back-end/plugins/nvault/src/INostrKeyEncoder.cs
+++ b/back-end/plugins/nvault/src/INostrKeyEncoder.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/plugins/nvault/src/INostrOperations.cs b/back-end/plugins/nvault/src/INostrOperations.cs
index efb5947..c3a3053 100644
--- a/back-end/plugins/nvault/src/INostrOperations.cs
+++ b/back-end/plugins/nvault/src/INostrOperations.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/plugins/nvault/src/INostrVault.cs b/back-end/plugins/nvault/src/INostrVault.cs
index 37bc05e..e710538 100644
--- a/back-end/plugins/nvault/src/INostrVault.cs
+++ b/back-end/plugins/nvault/src/INostrVault.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/plugins/nvault/src/ManagedCryptoprovider.cs b/back-end/plugins/nvault/src/ManagedCryptoprovider.cs
index 2eeae56..fb15b8f 100644
--- a/back-end/plugins/nvault/src/ManagedCryptoprovider.cs
+++ b/back-end/plugins/nvault/src/ManagedCryptoprovider.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/plugins/nvault/src/ManagedVaultClient.cs b/back-end/plugins/nvault/src/ManagedVaultClient.cs
index ff7977c..2e523c4 100644
--- a/back-end/plugins/nvault/src/ManagedVaultClient.cs
+++ b/back-end/plugins/nvault/src/ManagedVaultClient.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/plugins/nvault/src/Model/NostrContext.cs b/back-end/plugins/nvault/src/Model/NostrContext.cs
index b70a38e..15900e2 100644
--- a/back-end/plugins/nvault/src/Model/NostrContext.cs
+++ b/back-end/plugins/nvault/src/Model/NostrContext.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/plugins/nvault/src/Model/NostrEvent.cs b/back-end/plugins/nvault/src/Model/NostrEvent.cs
index ccd5a2c..9bbfd63 100644
--- a/back-end/plugins/nvault/src/Model/NostrEvent.cs
+++ b/back-end/plugins/nvault/src/Model/NostrEvent.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/plugins/nvault/src/Model/NostrKeyMeta.cs b/back-end/plugins/nvault/src/Model/NostrKeyMeta.cs
index 2e18ba7..3f0b985 100644
--- a/back-end/plugins/nvault/src/Model/NostrKeyMeta.cs
+++ b/back-end/plugins/nvault/src/Model/NostrKeyMeta.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/plugins/nvault/src/Model/NostrKeyMetaStore.cs b/back-end/plugins/nvault/src/Model/NostrKeyMetaStore.cs
index 96d0a3c..bfb6a26 100644
--- a/back-end/plugins/nvault/src/Model/NostrKeyMetaStore.cs
+++ b/back-end/plugins/nvault/src/Model/NostrKeyMetaStore.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/plugins/nvault/src/Model/NostrRelay.cs b/back-end/plugins/nvault/src/Model/NostrRelay.cs
index e80d268..ab8cea7 100644
--- a/back-end/plugins/nvault/src/Model/NostrRelay.cs
+++ b/back-end/plugins/nvault/src/Model/NostrRelay.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/plugins/nvault/src/Model/NostrRelayFlags.cs b/back-end/plugins/nvault/src/Model/NostrRelayFlags.cs
index 352ff11..29f3994 100644
--- a/back-end/plugins/nvault/src/Model/NostrRelayFlags.cs
+++ b/back-end/plugins/nvault/src/Model/NostrRelayFlags.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/plugins/nvault/src/Model/NostrRelayStore.cs b/back-end/plugins/nvault/src/Model/NostrRelayStore.cs
index 42f48ab..699124b 100644
--- a/back-end/plugins/nvault/src/Model/NostrRelayStore.cs
+++ b/back-end/plugins/nvault/src/Model/NostrRelayStore.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/plugins/nvault/src/NVault.csproj b/back-end/plugins/nvault/src/NVault.csproj
index fa82c90..d83dd2f 100644
--- a/back-end/plugins/nvault/src/NVault.csproj
+++ b/back-end/plugins/nvault/src/NVault.csproj
@@ -1,7 +1,7 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net6.0</TargetFramework>
+ <TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<EnableDynamicLoading>true</EnableDynamicLoading>
<PackageReadmeFile>README.md</PackageReadmeFile>
@@ -21,9 +21,9 @@
<ItemGroup>
<PackageReference Include="FluentValidation" Version="11.9.0" />
- <PackageReference Include="VNLib.Plugins.Extensions.Data" Version="0.1.0-ci0045" />
- <PackageReference Include="VNLib.Plugins.Extensions.Validation" Version="0.1.0-ci0045" />
- <PackageReference Include="VNLib.Plugins.Extensions.Loading.Sql" Version="0.1.0-ci0045" />
+ <PackageReference Include="VNLib.Plugins.Extensions.Data" Version="0.1.0-ci0047" />
+ <PackageReference Include="VNLib.Plugins.Extensions.Validation" Version="0.1.0-ci0047" />
+ <PackageReference Include="VNLib.Plugins.Extensions.Loading.Sql" Version="0.1.0-ci0047" />
</ItemGroup>
<ItemGroup>
@@ -36,5 +36,11 @@
</None>
</ItemGroup>
+ <ItemGroup>
+ <None Update="NVault.json">
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </None>
+ </ItemGroup>
+
</Project>
diff --git a/back-end/plugins/nvault/src/NativeSecp256k1Library.cs b/back-end/plugins/nvault/src/NativeSecp256k1Library.cs
index 2fcf447..abbafaf 100644
--- a/back-end/plugins/nvault/src/NativeSecp256k1Library.cs
+++ b/back-end/plugins/nvault/src/NativeSecp256k1Library.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/plugins/nvault/src/NostrEntry.cs b/back-end/plugins/nvault/src/NostrEntry.cs
index bdb78bb..2e57390 100644
--- a/back-end/plugins/nvault/src/NostrEntry.cs
+++ b/back-end/plugins/nvault/src/NostrEntry.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/plugins/nvault/src/NostrMessageKind.cs b/back-end/plugins/nvault/src/NostrMessageKind.cs
index 2a53928..507e941 100644
--- a/back-end/plugins/nvault/src/NostrMessageKind.cs
+++ b/back-end/plugins/nvault/src/NostrMessageKind.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/back-end/plugins/nvault/src/NostrOpProvider.cs b/back-end/plugins/nvault/src/NostrOpProvider.cs
index 5908e26..48ffe93 100644
--- a/back-end/plugins/nvault/src/NostrOpProvider.cs
+++ b/back-end/plugins/nvault/src/NostrOpProvider.cs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/extension/src/entries/background/main.ts b/extension/src/entries/background/main.ts
index 85e358a..a38eeff 100644
--- a/extension/src/entries/background/main.ts
+++ b/extension/src/entries/background/main.ts
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
@@ -25,6 +25,7 @@ import {
useEventTagFilterApi,
useInjectAllowList,
useMfaConfigApi,
+ usePermissionApi
} from "../../features";
import { useBackgroundFeatures } from "../../features/framework";
@@ -42,5 +43,6 @@ register([
usePkiApi,
useEventTagFilterApi,
useInjectAllowList,
- useMfaConfigApi
+ useMfaConfigApi,
+ usePermissionApi
]) \ No newline at end of file
diff --git a/extension/src/entries/background/script.js b/extension/src/entries/background/script.js
index b0211d1..2e3167b 100644
--- a/extension/src/entries/background/script.js
+++ b/extension/src/entries/background/script.js
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/extension/src/entries/background/serviceWorker.js b/extension/src/entries/background/serviceWorker.js
index b0211d1..2e3167b 100644
--- a/extension/src/entries/background/serviceWorker.js
+++ b/extension/src/entries/background/serviceWorker.js
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/extension/src/entries/contentScript/auth-popup.html b/extension/src/entries/contentScript/auth-popup.html
new file mode 100644
index 0000000..42a2ce3
--- /dev/null
+++ b/extension/src/entries/contentScript/auth-popup.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Authorize</title>
+ <link rel="stylesheet" href="auth-popup.css">
+ <script src="primary/main.js"></script>
+</head>
+ <div id="app"></div>
+</html> \ No newline at end of file
diff --git a/extension/src/entries/contentScript/primary/components/PromptPopup.vue b/extension/src/entries/contentScript/primary/components/PromptPopup.vue
index 156dfb8..1f62877 100644
--- a/extension/src/entries/contentScript/primary/components/PromptPopup.vue
+++ b/extension/src/entries/contentScript/primary/components/PromptPopup.vue
@@ -1,12 +1,12 @@
<template>
- <div v-show="isOpen" id="nvault-ext-prompt" :class="{'dark': darkMode }">
+ <div v-show="event" id="nvault-ext-prompt" :class="{'dark': darkMode }">
<div class="fixed top-0 bottom-0 left-0 right-0 text-white" style="z-index:9147483647 !important" >
<div class="fixed inset-0 left-0 w-full h-full bg-black/50" @click.self="close" />
- <div class="relative w-full max-w-[28rem] mx-auto mt-36 mb-auto" ref="prompt">
- <div class="w-full p-5 text-gray-800 bg-white border rounded-lg shadow-lg dark:bg-dark-900 dark:border-dark-500 dark:text-gray-200">
+ <div v-if="store.permissions.isPopup" class="relative w-full md:max-w-[28rem] mx-auto md:mt-36 mb-auto" ref="prompt">
+ <div class="w-full h-screen p-5 text-gray-800 bg-white border shadow-lg md:h-auto md:rounded dark:bg-dark-900 dark:border-dark-500 dark:text-gray-200">
<div v-if="loggedIn" class="">
<div class="flex flex-row justify-between">
@@ -44,7 +44,7 @@
<div class="py-3 text-sm text-center">
<span class="font-bold">{{ site }}</span>
- would like to access to
+ would like access to
<span class="font-bold">{{ event?.msg }}</span>
</div>
@@ -53,7 +53,12 @@
<button class="rounded btn sm" @click="close">Close</button>
</div>
<div>
- <button :disabled="selectedKey?.Id == undefined" class="rounded btn sm" @click="allow">Allow</button>
+ <button :disabled="selectedKey?.Id == undefined" class="rounded amber btn sm" @click="allow(true)">
+ Always Allow
+ </button>
+ </div>
+ <div>
+ <button :disabled="selectedKey?.Id == undefined" class="rounded btn sm" @click="allow(false)">Allow</button>
</div>
</div>
</div>
@@ -85,13 +90,16 @@
<script setup lang="ts">
import { ref } from 'vue'
-import { debugLog } from '@vnuge/vnlib.browser';
import { storeToRefs } from 'pinia';
import { computed } from 'vue';
import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'
import { clone, first } from 'lodash';
-import { usePrompt, type UserPermissionRequest } from '../../util'
import { useStore } from '../../../store';
+import { type PermissionRequest } from '../../../../features'
+
+interface PropmtMessage extends PermissionRequest{
+ msg: string;
+}
const store = useStore()
const { loggedIn, selectedKey, darkMode } = storeToRefs(store)
@@ -99,38 +107,38 @@ const keyName = computed(() => selectedKey.value?.UserName)
const prompt = ref(null)
-interface PopupEvent extends UserPermissionRequest {
- allow: () => void
- close: () => void
-}
-
-const evStack = ref<PopupEvent[]>([])
-const isOpen = computed(() => evStack.value.length > 0)
-const event = computed<PopupEvent | undefined>(() => first(evStack.value));
+const event = computed<PropmtMessage | undefined>(() => {
+ //Use a the current windowpending if set
+ if(store.permissions.windowPending){
+ return getPromptMessage(store.permissions.windowPending)
+ }
+ //Otherwise use the first pending event
+ const pending = first(store.permissions.pending)
+ return getPromptMessage(pending)
+});
const site = computed(() => new URL(event.value?.origin || "https://example.com").host)
const evData = computed(() => JSON.stringify(event.value || {}, null, 2))
-
const close = () => {
- //Pop the first event off
- const res = evStack.value.shift()
- res?.close()
+ if(event.value){
+ store.plugins.permission.deny(event.value.uuid);
+ }
}
-const allow = () => {
- //Pop the first event off
- const res = evStack.value.shift()
- res?.allow()
+
+const allow = (addRule: boolean) => {
+ if (event.value) {
+ store.plugins.permission.allow(event.value.uuid, addRule);
+ }
}
//Listen for events
-usePrompt((ev: UserPermissionRequest):Promise<boolean> => {
+const getPromptMessage = (perms: PermissionRequest | undefined): PropmtMessage | undefined => {
- ev = clone(ev)
+ if(!perms) return undefined
- debugLog('[usePrompt] =>', ev)
-
- switch(ev.type){
+ const ev = clone(perms) as PropmtMessage
+ switch(ev.requestType){
case 'getPublicKey':
ev.msg = "your public key"
break;
@@ -150,14 +158,7 @@ usePrompt((ev: UserPermissionRequest):Promise<boolean> => {
ev.msg = "unknown event"
break;
}
-
- return new Promise((resolve) => {
- evStack.value.push({
- ...ev,
- allow: () => resolve(true),
- close: () => resolve(false),
- })
- })
-})
+ return ev
+}
</script>
diff --git a/extension/src/entries/contentScript/primary/main.js b/extension/src/entries/contentScript/primary/main.js
index bbe5edf..15fc2ec 100644
--- a/extension/src/entries/contentScript/primary/main.js
+++ b/extension/src/entries/contentScript/primary/main.js
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
@@ -17,7 +17,7 @@ import { runtime } from "webextension-polyfill";
import { createApp } from "vue";
import { defer } from "lodash";
import { createPinia } from 'pinia';
-import { useBackgroundPiniaPlugin, identityPlugin, originPlugin } from '../../store'
+import { useBackgroundPiniaPlugin, identityPlugin, originPlugin, permissionsPlugin } from '../../store'
import { onLoad } from "../util";
import renderContent from "../renderContent";
import App from "./App.vue";
@@ -48,6 +48,7 @@ renderContent([], (appRoot, shadowRoot) => {
.use(bgPlugins)
.use(identityPlugin)
.use(originPlugin)
+ .use(permissionsPlugin)
//Add tailwind styles just to the shadow dom element
const style = document.createElement('style')
diff --git a/extension/src/entries/contentScript/renderContent.js b/extension/src/entries/contentScript/renderContent.js
index ca45c4f..1a72d45 100644
--- a/extension/src/entries/contentScript/renderContent.js
+++ b/extension/src/entries/contentScript/renderContent.js
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/extension/src/entries/contentScript/util.ts b/extension/src/entries/contentScript/util.ts
index 09b515a..aecb7b2 100644
--- a/extension/src/entries/contentScript/util.ts
+++ b/extension/src/entries/contentScript/util.ts
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
@@ -18,36 +18,12 @@ import { apiCall } from '@vnuge/vnlib.browser'
import { Store, storeToRefs } from 'pinia'
import { useScriptTag, watchOnce } from "@vueuse/core"
import { useStore } from '../store'
-
-export type PromptHandler = (request: UserPermissionRequest) => Promise<boolean>
-
-export interface UserPermissionRequest {
- type: string
- msg: string
- origin: string
- data: any
-}
-
-const _promptHandler = (() => {
- let _handler: PromptHandler | undefined = undefined;
- return {
- invoke(event:UserPermissionRequest){
- if (!_handler) {
- throw new Error('No prompt handler set')
- }
- return _handler(event)
- },
- set(handler: PromptHandler) {
- _handler = handler
- }
- }
-})()
-
+import { PrStatus } from '../../features'
const registerWindowHandler = (store: Store, extName: string) => {
const { selectedKey } = storeToRefs(store)
- const { nostr } = store.plugins;
+ const { nostr, permission } = store.plugins;
const onAsyncCall = async ({ source, data, origin } : MessageEvent<any>) => {
@@ -56,9 +32,9 @@ const registerWindowHandler = (store: Store, extName: string) => {
const requestPermission = async (cb: (...args: any) => Promise<any>) => {
//await propmt for user to allow the request
- const allow = await _promptHandler.invoke({ ...data, origin })
+ const allow = await permission.requestAndWaitResult({ ...data, requestType: data.type, origin })
//send request to background
- return allow ? await cb() : { error: 'User denied permission' }
+ return allow == PrStatus.Approved ? await cb() : { error: 'User denied permission' }
}
//Confirm the message format is correct
@@ -133,9 +109,7 @@ const registerWindowHandler = (store: Store, extName: string) => {
});
}
-export const usePrompt = (callback: PromptHandler) => _promptHandler.set(callback);
-
-export const onLoad = async (extName: string, scriptUrl: string) => {
+export const onLoad = (extName: string, scriptUrl: string) => {
const store = useStore()
const { isTabAllowed } = storeToRefs(store)
diff --git a/extension/src/entries/nostr-provider.js b/extension/src/entries/nostr-provider.js
index 9280b4d..1ec821d 100644
--- a/extension/src/entries/nostr-provider.js
+++ b/extension/src/entries/nostr-provider.js
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/extension/src/entries/options/App.vue b/extension/src/entries/options/App.vue
index d1da48e..3dbe94d 100644
--- a/extension/src/entries/options/App.vue
+++ b/extension/src/entries/options/App.vue
@@ -26,6 +26,11 @@
</Tab>
<Tab v-slot="{ selected }">
<button class="tab-title" :class="{ selected }">
+ Activity
+ </button>
+ </Tab>
+ <Tab v-slot="{ selected }">
+ <button class="tab-title" :class="{ selected }">
Privacy
</button>
</Tab>
@@ -64,15 +69,15 @@
<TabPanel class="mt-4">
<Identities :all-keys="allKeys" @edit-key="editKey"/>
</TabPanel>
- <TabPanel>
- <Account/>
- </TabPanel>
- <TabPanel>
- <Privacy/>
- </TabPanel>
- <TabPanel>
- <SiteSettings/>
- </TabPanel>
+
+ <TabPanel> <Account/> </TabPanel>
+
+ <TabPanel> <EventHistory/> </TabPanel>
+
+ <TabPanel> <Privacy/> </TabPanel>
+
+ <TabPanel> <SiteSettings/> </TabPanel>
+
<TabPanel>
<div class="flex flex-col px-2 mt-4">
<div class="absolute mx-auto">
@@ -130,6 +135,7 @@ import { useStore } from "../store";
import Account from "./components/Account.vue";
import ConfirmPrompt from "../../components/ConfirmPrompt.vue";
import PasswordPrompt from "../../components/PasswordPrompt.vue";
+import EventHistory from "./components/EventHistory.vue";
//Configure the notifier to use the notification library
@@ -143,7 +149,7 @@ const keyBuffer = ref<NostrPubKey>({} as NostrPubKey)
const editKey = (key: NostrPubKey) =>{
//Goto hidden tab
- selectedTab.value = 4
+ selectedTab.value = 5
//Set selected key
keyBuffer.value = { ...key }
}
diff --git a/extension/src/entries/options/components/AutoRules.vue b/extension/src/entries/options/components/AutoRules.vue
new file mode 100644
index 0000000..16fddd3
--- /dev/null
+++ b/extension/src/entries/options/components/AutoRules.vue
@@ -0,0 +1,119 @@
+<template>
+ <div class="">
+ <div class="flex flex-row justify-between mt-16">
+ <div class="font-bold">
+ Approval Rules
+ </div>
+ <div class="flex justify-center">
+ <nav aria-label="Pagination">
+ <ul class="inline-flex items-center space-x-1 text-sm rounded-md">
+ <li>
+ <button @click="prev" class="page-btn">
+ <fa-icon icon="chevron-left" class="w-4" />
+ </button>
+ </li>
+ <li>
+ <span class="inline-flex items-center px-4 py-2 space-x-1 rounded-md">
+ Page
+ <b class="mx-1">{{ currentPage }}</b>
+ of
+ <b class="ml-1">{{ pageCount }}</b>
+ </span>
+ </li>
+ <li>
+ <button @click="next" class="page-btn">
+ <fa-icon icon="chevron-right" class="w-4" />
+ </button>
+ </li>
+ </ul>
+ </nav>
+ </div>
+ </div>
+ <div class="">
+ <table class="min-w-full text-sm divide-y-2 divide-gray-200 dark:divide-dark-500">
+ <thead class="text-left bg-gray-50 dark:bg-dark-700">
+ <tr>
+ <th class="p-2 font-medium whitespace-nowrap dark:text-white">
+ Rule
+ </th>
+ <th class="p-2 font-medium whitespace-nowrap dark:text-white">
+ Origin
+ </th>
+ <th class="p-2 font-medium whitespace-nowrap dark:text-white">
+ Time
+ </th>
+ <th class="p-2"></th>
+ </tr>
+ </thead>
+
+ <tbody class="divide-y divide-gray-200 dark:divide-dark-500">
+ <tr v-for="rule in currentRulePage" :key="rule.timestamp" class="">
+ <td class="p-2 t font-medium truncate max-w-[8rem] whitespace-nowrap ">
+ {{ rule.type }}
+ </td>
+ <td class="p-2 whitespace-nowrap">
+ {{ rule.origin }}
+ </td>
+ <td class="p-2 whitespace-nowrap">
+ {{ createShortDateAndTime(rule) }}
+ </td>
+ <td class="p-2 text-right whitespace-nowrap">
+ <div class="button-group">
+ <button class="rounded btn xs" @click="deleteRule(rule)">
+ Revoke
+ </button>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</template>
+
+<script setup lang="ts">
+import { computed } from 'vue';
+import { get, useOffsetPagination } from '@vueuse/core';
+import { } from '@headlessui/vue'
+import { useStore } from '../../store';
+import { storeToRefs } from 'pinia';
+import { slice } from 'lodash';
+import { type AutoAllowRule } from '../../../features'
+
+const store = useStore()
+const { } = storeToRefs(store)
+
+const rules = computed(() => store.permissions.rules)
+
+const { next, prev, currentPage, currentPageSize, pageCount } = useOffsetPagination({
+ pageSize: 10,
+ total: computed(() => rules.value.length)
+})
+
+const currentRulePage = computed(() => {
+ const start = (get(currentPage) - 1) * get(currentPageSize)
+ const end = start + 10
+ return slice(rules.value, start, end)
+})
+
+const deleteRule = (rule: AutoAllowRule) => {
+ store.plugins.permission.deleteRule(rule)
+}
+
+const createShortDateAndTime = (request: { timestamp: number}) => {
+ const date = new Date(request.timestamp)
+ const hours = date.getHours()
+ const minutes = date.getMinutes()
+ const seconds = date.getSeconds()
+ const day = date.getDate()
+ const month = date.getMonth() + 1
+ const year = date.getFullYear()
+ return `${month}/${day}/${year} ${hours}:${minutes}:${seconds}`
+}
+
+
+</script>
+
+<style lang="scss">
+
+</style> \ No newline at end of file
diff --git a/extension/src/entries/options/components/EvHistoryTable.vue b/extension/src/entries/options/components/EvHistoryTable.vue
new file mode 100644
index 0000000..6ea6cac
--- /dev/null
+++ b/extension/src/entries/options/components/EvHistoryTable.vue
@@ -0,0 +1,84 @@
+<template>
+ <table class="min-w-full divide-y-2 divide-gray-200 dark:divide-dark-500">
+ <thead class="text-left bg-gray-50 dark:bg-dark-700">
+ <tr>
+ <th class="p-2 font-medium whitespace-nowrap dark:text-white">
+ Type
+ </th>
+ <th class="p-2 font-medium whitespace-nowrap dark:text-white">
+ Origin
+ </th>
+ <th class="p-2 font-medium whitespace-nowrap dark:text-white">
+ Time
+ </th>
+ <th class="p-2"></th>
+ </tr>
+ </thead>
+
+ <tbody class="divide-y divide-gray-200 dark:divide-dark-500">
+ <tr v-for="req in requests" :key="req.uuid" class="">
+ <td class="p-2 t font-medium truncate max-w-[8rem] whitespace-nowrap ">
+ {{ req.requestType }}
+ </td>
+ <td class="p-2 whitespace-nowrap">
+ {{ req.origin }}
+ </td>
+ <td class="p-2 whitespace-nowrap">
+ {{ createShortDateAndTime(req) }}
+ </td>
+ <td class="p-2 text-right whitespace-nowrap">
+ <div v-if="!readonly" class="button-group">
+ <button class="rounded btn xs" @click="approve(req)">
+ <fa-icon icon="check" class="inline" />
+ </button>
+ <button class="rounded btn red xs" @click="deny(req)">
+ <fa-icon icon="trash-can" class="inline" />
+ </button>
+ </div>
+ <div v-else class="text-sm font-bold">
+ {{ statusToString(req.status) }}
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+</template>
+
+<script setup lang="ts">
+import { toRefs } from 'vue';
+import { PermissionRequest, PrStatus } from '../../../features';
+
+const emit = defineEmits(['deny', 'approve'])
+const props = defineProps<{
+ requests: PermissionRequest[],
+ readonly: boolean
+}>()
+
+const { requests, readonly } = toRefs(props)
+
+const createShortDateAndTime = (request: PermissionRequest) => {
+ const date = new Date(request.timestamp)
+ const hours = date.getHours()
+ const minutes = date.getMinutes()
+ const seconds = date.getSeconds()
+ const day = date.getDate()
+ const month = date.getMonth() + 1
+ const year = date.getFullYear()
+ return `${month}/${day}/${year} ${hours}:${minutes}:${seconds}`
+}
+
+const deny = (request: PermissionRequest) => emit('deny', request)
+const approve = (request: PermissionRequest) => emit('approve', request)
+
+const statusToString = (status: PrStatus) => {
+ switch(status) {
+ case PrStatus.Approved:
+ return 'Approved'
+ case PrStatus.Denied:
+ return 'Denied'
+ case PrStatus.Pending:
+ return 'Pending'
+ }
+}
+
+</script> \ No newline at end of file
diff --git a/extension/src/entries/options/components/EventHistory.vue b/extension/src/entries/options/components/EventHistory.vue
new file mode 100644
index 0000000..0711ae6
--- /dev/null
+++ b/extension/src/entries/options/components/EventHistory.vue
@@ -0,0 +1,132 @@
+<template>
+ <div id="ev-history" class="flex flex-col w-full mt-4 sm:px-2">
+ <form @submit.prevent="">
+ <div class="w-full max-w-xl mx-auto">
+ <h3 class="text-center">
+ Permissions
+ </h3>
+
+ <div class="flex flex-row justify-between mt-4">
+ <div class="font-bold">
+ Pending
+ </div>
+ <div class="flex justify-center">
+ </div>
+ </div>
+
+ <div class="my-6 ">
+ <EvHistoryTable :readonly="false" :requests="pending" @deny="deny" @approve="approve" />
+ </div>
+
+ <AutoRules />
+
+ <div class="flex flex-row justify-between mt-16">
+ <div class="font-bold">
+ History
+ </div>
+ <div class="flex justify-center">
+ <nav aria-label="Pagination">
+ <ul class="inline-flex items-center space-x-1 text-sm rounded-md">
+ <li>
+ <button @click="prev" class="page-btn">
+ <fa-icon icon="chevron-left" class="w-4" />
+ </button>
+ </li>
+ <li>
+ <span class="inline-flex items-center px-4 py-2 space-x-1 rounded-md">
+ Page
+ <b class="mx-1">{{ currentPage }}</b>
+ of
+ <b class="ml-1">{{ pageCount }}</b>
+ </span>
+ </li>
+ <li>
+ <button @click="next" class="page-btn">
+ <fa-icon icon="chevron-right" class="w-4" />
+ </button>
+ </li>
+ </ul>
+ </nav>
+ </div>
+ </div>
+
+ <div class="mt-1">
+ <EvHistoryTable :readonly="true" :requests="evHistoryCurrentPage" @deny="deny" @approve="approve" />
+ </div>
+
+ <div class="mt-4 ml-auto w-fit">
+ <button class="rounded btn sm red" @click="clearHistory">
+ Delete History
+ </button>
+ </div>
+ </div>
+ </form>
+ </div>
+</template>
+
+<script setup lang="ts">
+import { useConfirm } from '@vnuge/vnlib.browser';
+import { computed } from 'vue';
+import { get, useOffsetPagination } from '@vueuse/core';
+import { } from '@headlessui/vue'
+import { useStore } from '../../store';
+import { PermissionRequest, PrStatus } from '../../../features';
+import EvHistoryTable from './EvHistoryTable.vue';
+import { filter, slice } from 'lodash';
+import AutoRules from './AutoRules.vue';
+
+const store = useStore()
+const { reveal } = useConfirm()
+
+const pending = computed(() => store.permissions.pending)
+const notPending = computed(() => filter(store.permissions.all, r => r.status !== PrStatus.Pending))
+
+const deny = (request: PermissionRequest) => {
+ if(request.status !== PrStatus.Pending) return
+ //push deny to store
+ store.plugins.permission.deny(request.uuid)
+}
+
+const approve = (request: PermissionRequest) => {
+ if(request.status !== PrStatus.Pending) return
+ //push allow to store
+ store.plugins.permission.allow(request.uuid, false)
+}
+
+const { next, prev, currentPage, currentPageSize, pageCount } = useOffsetPagination({
+ pageSize: 10,
+ total: computed(() => notPending.value.length)
+})
+
+const evHistoryCurrentPage = computed(() => {
+ const start = (get(currentPage) - 1) * get(currentPageSize)
+ const end = start + 10
+ return slice(notPending.value, start, end)
+})
+
+const clearHistory = async () => {
+ const { isCanceled } = await reveal({
+ title: 'Clear History',
+ text: 'Are you sure you want to clear your event history?',
+ })
+
+ if(isCanceled) return
+
+ //Clear all history
+ store.plugins.permission.clearRequests()
+}
+
+</script>
+
+<style lang="scss">
+#ev-history{
+ button.page-btn{
+ @apply inline-flex items-center px-2 py-2 space-x-2 font-medium rounded-full;
+ @apply bg-white border border-gray-300 rounded-full hover:bg-gray-50 dark:bg-dark-600 dark:hover:bg-dark-500 dark:border-dark-300;
+ }
+
+ form tr {
+ @apply sm:text-sm text-xs dark:text-gray-400 text-gray-600;
+ }
+}
+</style> \ No newline at end of file
diff --git a/extension/src/entries/options/main.js b/extension/src/entries/options/main.js
index 7747735..3dd01cb 100644
--- a/extension/src/entries/options/main.js
+++ b/extension/src/entries/options/main.js
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
@@ -22,12 +22,12 @@ import Notifications from "@kyvg/vue3-notification";
/* FONT AWESOME CONFIG */
import { library } from '@fortawesome/fontawesome-svg-core'
-import { faChevronLeft, faChevronRight, faCopy, faDownload, faEdit, faExternalLinkAlt, faLock, faLockOpen, faMinusCircle, faMoon, faPlus, faRefresh, faSun, faTrash, faTrashCan } from '@fortawesome/free-solid-svg-icons'
+import { faCheck, faChevronLeft, faChevronRight, faCopy, faDownload, faEdit, faExternalLinkAlt, faLock, faLockOpen, faMinusCircle, faMoon, faPlus, faRefresh, faSun, faTrash, faTrashCan } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import { createPinia } from "pinia";
-import { identityPlugin, mfaConfigPlugin, originPlugin, useBackgroundPiniaPlugin } from "../store";
+import { identityPlugin, mfaConfigPlugin, originPlugin, permissionsPlugin, useBackgroundPiniaPlugin } from "../store";
-library.add(faCopy, faEdit, faChevronLeft, faMoon, faSun, faLock, faLockOpen, faExternalLinkAlt, faTrash, faDownload, faChevronRight, faPlus, faRefresh, faTrashCan, faMinusCircle)
+library.add(faCopy, faEdit, faChevronLeft, faMoon, faSun, faLock, faLockOpen, faExternalLinkAlt, faTrash, faDownload, faChevronRight, faPlus, faRefresh, faTrashCan, faMinusCircle ,faTrashCan, faCheck)
//Create the background feature wiring
const bgPlugins = useBackgroundPiniaPlugin('options')
@@ -37,6 +37,7 @@ const pinia = createPinia()
.use(identityPlugin) //Add the identity plugin
.use(originPlugin) //Add the origin plugin
.use(mfaConfigPlugin) //Add the mfa config plugin
+ .use(permissionsPlugin)
createApp(App)
.use(Notifications)
diff --git a/extension/src/entries/popup/Components/PageContent.vue b/extension/src/entries/popup/Components/PageContent.vue
index 8a48840..37e119d 100644
--- a/extension/src/entries/popup/Components/PageContent.vue
+++ b/extension/src/entries/popup/Components/PageContent.vue
@@ -44,7 +44,7 @@
</div>
<div class="">
- <label class="mb-0.5 text-sm dark:text-dark-100">
+ <label class="mb-0.5 text-sm">
Identity
</label>
<IdentitySelection></IdentitySelection>
@@ -64,12 +64,12 @@
</div>
<div class="mt-4">
- <label class="block mb-1 text-xs text-left dark:text-dark-100" >
+ <label class="block mb-1 text-xs text-left " >
Current origin
</label>
<div v-if="isOriginProtectionOn" class="flex flex-row w-full gap-2">
- <input :value="currentOrigin" class="flex-1 p-1 mx-0 text-sm input" readonly/>
+ <input :value="currentOrigin" class="flex-1 p-1 mx-0 text-sm input dark:text-dark-100" readonly/>
<button v-if="isTabAllowed" class="btn xs" @click="store.dissallowOrigin()">
<fa-icon icon="minus" />
@@ -79,10 +79,20 @@
</button>
</div>
- <div v-else class="text-xs text-center">
+ <div v-else class="text-xs text-center dark:text-dark-100">
<span class="">Tracking protection disabled</span>
</div>
</div>
+ <div class="mt-4">
+ <label class="block mb-1 text-xs text-left " >
+ Permissions
+ </label>
+ <ul class="flex flex-row flex-wrap gap-2 dark:text-dark-100">
+ <li v-for="rule in ruleTypes" :key="rule" class="text-xs">
+ {{ rule }}
+ </li>
+ </ul>
+ </div>
</div>
</div>
@@ -102,6 +112,7 @@ import { notify } from "@kyvg/vue3-notification";
import { runtime } from "webextension-polyfill";
import Login from "./Login.vue";
import IdentitySelection from "./IdentitySelection.vue";
+import { map } from "lodash";
configureNotifier({notify, close:notify.close})
@@ -111,6 +122,8 @@ const { copy, copied } = useClipboard()
const pubKey = computed(() => selectedKey!.value?.PublicKey)
+const ruleTypes = computed<string[]>(() => map(store.permissions.rulesForCurrentOrigin, 'type'))
+
const openOptions = () => runtime.openOptionsPage();
const toggleDark = () => store.toggleDarkMode()
diff --git a/extension/src/entries/popup/main.js b/extension/src/entries/popup/main.js
index c8e9ef8..6642262 100644
--- a/extension/src/entries/popup/main.js
+++ b/extension/src/entries/popup/main.js
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
@@ -15,7 +15,7 @@
import { createApp } from "vue";
import { createPinia } from "pinia";
-import { identityPlugin, originPlugin, useBackgroundPiniaPlugin } from '../store'
+import { identityPlugin, originPlugin, permissionsPlugin, useBackgroundPiniaPlugin } from '../store'
import App from "./App.vue";
import Notifications from "@kyvg/vue3-notification";
import '@fontsource/noto-sans-masaram-gondi'
@@ -35,6 +35,7 @@ const pinia = createPinia()
.use(bgPlugin) //Add the background pinia plugin
.use(identityPlugin)
.use(originPlugin)
+ .use(permissionsPlugin)
createApp(App)
.use(Notifications)
diff --git a/extension/src/entries/store/features.ts b/extension/src/entries/store/features.ts
index 9f9a4db..ad83e16 100644
--- a/extension/src/entries/store/features.ts
+++ b/extension/src/entries/store/features.ts
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
@@ -30,7 +30,8 @@ import {
useEventTagFilterApi,
useInjectAllowList,
onWatchableChange,
- useMfaConfigApi
+ useMfaConfigApi,
+ usePermissionApi
} from "../../features"
import { ChannelContext } from '../../messaging'
@@ -59,7 +60,8 @@ const usePlugins = (context: ChannelContext) => {
pki: use(usePkiApi),
tagFilter: use(useEventTagFilterApi),
allowedOrigins: use(useInjectAllowList),
- mfaConfig: use(useMfaConfigApi)
+ mfaConfig: use(useMfaConfigApi),
+ permission: use(usePermissionApi)
}
}
diff --git a/extension/src/entries/store/identity.ts b/extension/src/entries/store/identity.ts
index 5bbc67a..ade7c94 100644
--- a/extension/src/entries/store/identity.ts
+++ b/extension/src/entries/store/identity.ts
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/extension/src/entries/store/index.ts b/extension/src/entries/store/index.ts
index e3eef2f..8be57ff 100644
--- a/extension/src/entries/store/index.ts
+++ b/extension/src/entries/store/index.ts
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
@@ -24,6 +24,7 @@ export * from './allowedOrigins'
export * from './features'
export * from './identity'
export * from './mfaconfig'
+export * from './permissions'
export const useStore = defineStore({
id: 'main',
diff --git a/extension/src/entries/store/mfaconfig.ts b/extension/src/entries/store/mfaconfig.ts
index 6a5116d..bd8ef83 100644
--- a/extension/src/entries/store/mfaconfig.ts
+++ b/extension/src/entries/store/mfaconfig.ts
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/extension/src/entries/store/permissions.ts b/extension/src/entries/store/permissions.ts
new file mode 100644
index 0000000..6b011de
--- /dev/null
+++ b/extension/src/entries/store/permissions.ts
@@ -0,0 +1,105 @@
+// Copyright (C) 2024 Vaughn Nugent
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+import 'pinia'
+import { filter, find } from 'lodash'
+import { PiniaPluginContext, storeToRefs } from 'pinia'
+import { computed, shallowRef } from 'vue'
+
+import {
+ PrStatus,
+ onWatchableChange,
+ PermissionRequest
+} from "../../features"
+import { get } from '@vueuse/core'
+import { AutoAllowRule } from '../../features/permissions'
+
+export interface PermissionApi {
+ readonly pending: PermissionRequest[]
+ readonly all: PermissionRequest[]
+ readonly windowPending: PermissionRequest | undefined
+ readonly isPopup: boolean
+ readonly rules: AutoAllowRule[],
+ readonly rulesForCurrentOrigin: AutoAllowRule[]
+}
+
+declare module 'pinia' {
+ export interface PiniaCustomProperties {
+ permissions: PermissionApi
+ }
+}
+
+export const permissionsPlugin = ({ store }: PiniaPluginContext) => {
+
+ const { permission } = store.plugins
+
+ const { currentOrigin } = storeToRefs(store)
+
+ const all = shallowRef<PermissionRequest[]>([])
+ const activeRequests = computed(() => filter(all.value, r => r.status == PrStatus.Pending))
+ const windowPending = shallowRef<PermissionRequest | undefined>()
+ const rules = shallowRef<AutoAllowRule[]>([])
+
+ const rulesForCurrentOrigin = computed(() => filter(rules.value, r => r.origin == get(currentOrigin)))
+
+ const closeIfPopup = () => {
+ const windowQueryArgs = new URLSearchParams(window.location.search)
+ if (windowQueryArgs.has("closeable")) {
+ window.close()
+ }
+ }
+
+ const getPendingWindowRequest = () => {
+ const uuid = getWindowUuid()
+ const req = get(activeRequests)
+ return find(req, r => r.uuid == uuid)
+ }
+
+ const getWindowUuid = () => {
+ const queryArgs = new URLSearchParams(window.location.search)
+ return queryArgs.get("uuid")
+ }
+
+ //watch for status changes
+ onWatchableChange(permission, async () => {
+ //get latest requests and current ruleset
+ all.value = await permission.getRequests()
+ rules.value = await permission.getRules()
+
+ //update window pending request
+ windowPending.value = getPendingWindowRequest()
+
+ //if there are no more pending requests, close the popup
+ if (activeRequests.value.length == 0) {
+ closeIfPopup()
+ }
+
+ //If the window's request is no longer pending, close the popup
+ if (getWindowUuid() && !get(windowPending)){
+ closeIfPopup()
+ }
+
+ }, { immediate: true })
+
+ return {
+ permissions:{
+ all,
+ rules,
+ rulesForCurrentOrigin,
+ pending: activeRequests,
+ isPopup: getWindowUuid() !== null,
+ }
+ }
+} \ No newline at end of file
diff --git a/extension/src/features/framework/index.ts b/extension/src/features/framework/index.ts
index 2d9cad5..b545335 100644
--- a/extension/src/features/framework/index.ts
+++ b/extension/src/features/framework/index.ts
@@ -1,5 +1,5 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/extension/src/features/history.ts b/extension/src/features/history.ts
index ff7c267..82f31c4 100644
--- a/extension/src/features/history.ts
+++ b/extension/src/features/history.ts
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/extension/src/features/identity-api.ts b/extension/src/features/identity-api.ts
index d909162..0b8973d 100644
--- a/extension/src/features/identity-api.ts
+++ b/extension/src/features/identity-api.ts
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/extension/src/features/index.ts b/extension/src/features/index.ts
index f05de9b..0a8e182 100644
--- a/extension/src/features/index.ts
+++ b/extension/src/features/index.ts
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
@@ -21,6 +21,8 @@ export type { PkiPubKey, EcKeyParams, LocalPkiApi as PkiApi } from './pki-api'
export type { NostrApi } from './nostr-api'
export type { UserApi } from './auth-api'
export type { IdentityApi } from './identity-api'
+export type { MfaUpdateResult } from './mfa-api'
+export type { PermissionRequest, PrType, AutoAllowRule } from './permissions'
export { useBackgroundFeatures, useForegoundFeatures } from './framework'
export { useLocalPki, usePkiApi } from './pki-api'
@@ -32,4 +34,5 @@ export { useHistoryApi } from './history'
export { useEventTagFilterApi } from './tagfilter-api'
export { useInjectAllowList } from './nip07allow-api'
export { onWatchableChange } from './util'
-export { useMfaConfigApi, type MfaUpdateResult } from './mfa-api' \ No newline at end of file
+export { useMfaConfigApi } from './mfa-api'
+export { usePermissionApi, PrStatus } from './permissions' \ No newline at end of file
diff --git a/extension/src/features/mfa-api.ts b/extension/src/features/mfa-api.ts
index fc6d51a..85ff49e 100644
--- a/extension/src/features/mfa-api.ts
+++ b/extension/src/features/mfa-api.ts
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/extension/src/features/nip07allow-api.ts b/extension/src/features/nip07allow-api.ts
index 8c08d5e..4787437 100644
--- a/extension/src/features/nip07allow-api.ts
+++ b/extension/src/features/nip07allow-api.ts
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/extension/src/features/nostr-api.ts b/extension/src/features/nostr-api.ts
index 046ccea..4aee660 100644
--- a/extension/src/features/nostr-api.ts
+++ b/extension/src/features/nostr-api.ts
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/extension/src/features/permissions.ts b/extension/src/features/permissions.ts
index c06257b..5473962 100644
--- a/extension/src/features/permissions.ts
+++ b/extension/src/features/permissions.ts
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
@@ -13,68 +13,353 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
-import { useStorageAsync } from "@vueuse/core";
-import { find, isEmpty, merge, remove } from "lodash";
-import { storage } from "webextension-polyfill";
-import { useAuthApi } from "./auth-api";
-import { useSettingsApi } from "./settings";
+import { Mutable, get, set, toRefs } from "@vueuse/core";
+import { Ref } from "vue";
+import { defaultTo, defaults, defer, filter, find, forEach, isEqual, isNil } from "lodash";
+import { nanoid } from "nanoid";
+import { useSession } from "@vnuge/vnlib.browser";
+import { type FeatureApi, type BgRuntime, type IFeatureExport, exportForegroundApi, optionsOnly } from "./framework";
+import { waitForChangeFn, waitOne } from "./util";
+import { windows, runtime, Windows, tabs } from "webextension-polyfill";
+import type { TotpUpdateMessage, Watchable } from "./types";
+import type { AppSettings } from "./settings";
-const permissions = useStorageAsync("permissions", [], storage.local);
+export interface AutoAllowRule{
+ origin: string
+ type: string
+ readonly timestamp: number
+}
-export const setAutoAllow = async (origin, mKind, keyId) => {
- permissions.value.push({ origin, mKind, keyId, })
+export type PrType = string
+export enum PrStatus{
+ Pending,
+ Approved,
+ Denied
}
-/**
- * Determines if the user has previously allowed the origin to use the key to sign events
- * of the desired kind
- * @param {*} origin The site origin requesting the permission
- * @param {*} mKind The kind of message being signed
- * @param {*} keyId The keyId of the key being used to sign the message
- */
-export const isAutoAllow = async (origin, mKind, keyId) => {
- return find(permissions.value, p => p.origin === origin && p.mKind === mKind && p.keyId === keyId) !== undefined
+export interface PermissionRequest{
+ readonly uuid: string
+ readonly origin: string
+ readonly requestType: PrType
+ readonly timestamp: number
+ readonly status: PrStatus
}
-/**
- * Removes the auto allow permission from the list
- * @param {*} origin The site origin requesting the permission
- * @param {*} mKind The message kind being signed
- * @param {*} keyId The keyId of the key being used to sign the message
- */
-export const removeAutoAllow = async (origin, mKind, keyId) => {
- //Remove the permission from the list
- remove(permissions.value, p => p.origin === origin && p.mKind === mKind && p.keyId === keyId);
+export type MfaUpdateResult = TotpUpdateMessage
+
+export interface PermissionApi extends FeatureApi, Watchable {
+ getRequests(): Promise<PermissionRequest[]>
+ allow(requestId: string, addRule: boolean): Promise<void>
+ deny(requestId: string): Promise<void>
+ clearRequests(): Promise<void>
+ requestAndWaitResult(request: Partial<PermissionRequest>): Promise<PrStatus>
+
+ getRules(): Promise<AutoAllowRule[]>
+ deleteRule(rule: AutoAllowRule): Promise<void>
+ addRule(rule: AutoAllowRule): Promise<void>
}
+interface PermissionSlot {
+ requests: PermissionRequest[]
+}
-export const useSitePermissions = (() => {
+interface RuleSlot{
+ rules: AutoAllowRule[]
+}
- const { apiCall, handleProtectedMessage } = useAuthApi();
- const { currentConfig } = useSettingsApi();
+const useRuleSet = (slot: Ref<RuleSlot>) => {
+ defaults(slot.value, { rules: [] })
+ const { rules } = toRefs(slot)
+
+ return{
+ isAllowed: (request: PermissionRequest): boolean => {
+ //find existing rule
+ const rule = find(get(rules), r => isEqual(r.origin, request.origin) && isEqual(r.type, request.requestType))
+ return !isNil(rule)
+ },
+ addRule: (rule: Partial<AutoAllowRule>) => {
+ const current = defaultTo(get(rules), [])
+
+ //see if rule aready exists
+ if (find(current, r => isEqual(r.origin, rule.origin) && isEqual(r.type, rule.type))) {
+ return;
+ }
+
+ //add rule to head of store
+ current.unshift({
+ ...rule,
+ timestamp: Date.now()
+ } as AutoAllowRule)
+
+ set(rules, current)
+ },
+ deleteRule (rule: AutoAllowRule) {
+ //Filter all non matching rules
+ const wo = filter(get(rules), r => !(isEqual(r.origin, rule.origin) && isEqual(r.type, rule.type)))
+ set(rules, wo)
+ },
+ getRules:(): AutoAllowRule[] =>get(rules)
+ }
+}
+
+const usePermissions = (slot: Ref<PermissionSlot>, rules: ReturnType<typeof useRuleSet>) => {
+
+ const permPopupUrl = runtime.getURL("src/entries/contentScript/auth-popup.html")
+
+ defaults(slot.value, { rules: [] })
+ const { requests } = toRefs(slot)
+
+ const drawWindow = async ({ uuid }: Partial<PermissionRequest>): Promise<Windows.CreateCreateDataType> => {
+ const current = await windows.getCurrent()
+
+ const minWidth = 350
+ const minHeight = 180
+
+ const maxWidth = 500
+ const maxHeight = 250
- const getCurrentPerms = async () => {
- const { permissions } = await storage.local.get('permissions');
+ const width = Math.min(Math.max(current.width! - 100, minWidth), maxWidth)
+ const height = Math.min(Math.max(current.height! - 100, minHeight), maxHeight)
- //Store a default config if none exists
- if (isEmpty(permissions)) {
- await storage.local.set({ siteConfig: defaultConfig });
+ //draw half way across screen minus half its width
+ const left = current.left! + (current.width! / 2) - (width / 2)
+
+ return {
+ url: `${permPopupUrl}?uuid=${uuid}&closeable`,
+ type: "popup",
+ height: height,
+ width: width,
+ focused: true,
+ allowScriptsToClose: true,
+ top: 100,
+ //try to center popup
+ left: left,
}
+ }
+
+ const activePopups = new Map<number, PermissionRequest>()
- //Merge the default config with the site config
- return merge(defaultConfig, siteConfig)
+ const getRequest = (requestId: string): PermissionRequest | undefined => {
+ return find(get(requests), r => r.uuid === requestId)
}
- const onIsSiteEnabled = handleProtectedMessage(async (data) => {
+ const updateRequest = (request: PermissionRequest, addRule: boolean) => {
+ const current = get(requests)
- })
+ const index = current.findIndex(r => r.uuid === request.uuid)
+ if (index === -1) {
+ throw new Error("Request not found")
+ }
+
+ //Set request state
+ current[index] = request
+
+ //Update storage
+ set(requests, current)
- return () => {
+ //Add rule if needed
+ if (addRule) {
+ rules.addRule({ origin: request.origin, type: request.requestType })
+ }
+ }
+
+ const initNewRequest = (request: Partial<PermissionRequest>): PermissionRequest => {
return {
- onCreateIdentity,
- onUpdateIdentity
+ ...request,
+ uuid: nanoid(),
+ status: PrStatus.Pending,
+ timestamp: Date.now()
+ } as PermissionRequest
+ }
+
+ //Listen for popup close to cleanup request
+ windows.onRemoved.addListener(async (id) => {
+ const req = activePopups.get(id)
+ if (req && req.status === PrStatus.Pending) {
+ //set denied
+ (req as Mutable<PermissionRequest>).status = PrStatus.Denied
+ //popup closed, set to denied
+ updateRequest(req, false)
}
+ })
+
+ //Watch for changes to the current tab
+ tabs.onRemoved.addListener(async (tabId) => {
+ const tab = await tabs.get(tabId)
+ const { origin } = new URL(tab.url!)
+
+ //Find ally pending requests for the origin
+ const pending = filter(requests.value, r => r.status == PrStatus.Pending && r.origin == origin)
+
+ //update all pending requests to denied
+ forEach(pending, r => updateRequest(r, false))
+ })
+
+ return{
+ getRequest,
+
+ async showPermsWindow (request: PermissionRequest): Promise<void> {
+ const windowsArgs = await drawWindow(request)
+ const { id } = await windows.create(windowsArgs)
+ activePopups.set(id!, request)
+ },
+
+ pushRequest (request: Partial<PermissionRequest>, showPopup: boolean): PermissionRequest {
+ //Create new request
+ const req = initNewRequest(request)
+
+ //See if allowed
+ if(rules.isAllowed(req)){
+ //Set to approved
+ (req as Mutable<PermissionRequest>).status = PrStatus.Approved
+ //No need to show popup
+ showPopup = false
+ }
+
+ const current = get(requests)
+ current.unshift(req)
+ set(requests, current)
+
+ //Show popup if needed
+ if (showPopup) {
+ this.showPermsWindow(req)
+ }
+
+ return req
+ },
+
+ allow (requestId: string, addRule: boolean): void {
+ const request = getRequest(requestId)
+ if(!request){
+ throw new Error("Request not found")
+ }
+ //set approved
+ (request as Mutable<PermissionRequest>).status = PrStatus.Approved
+ //update request
+ updateRequest(request, addRule)
+ },
+
+ deny(requestId: string): void {
+ const request = getRequest(requestId)
+ if (!request) {
+ throw new Error("Request not found")
+ }
+ //set denied
+ (request as Mutable<PermissionRequest>).status = PrStatus.Denied
+ //update request
+ updateRequest(request, false)
+ },
+
+ clearAll: () => {
+ //notify pending requests
+ forEach(filter(requests.value, r => r.status == PrStatus.Pending), r => {
+ //set denied
+ (r as Mutable<PermissionRequest>).status = PrStatus.Denied
+ //update request
+ updateRequest(r, false)
+ })
+
+ //Then defer clear
+ defer(() => set(requests, []))
+ },
+ getAll: () => get(requests)
}
+}
+
+export const usePermissionApi = (): IFeatureExport<AppSettings, PermissionApi> => {
+
+ return {
+ background: ({ state }: BgRuntime<AppSettings>): PermissionApi => {
+ const { loggedIn } = useSession();
+ const { currentConfig } = state
+
+ //Open storage slot for permissions
+ const reqStore = state.useStorageSlot<PermissionSlot>("permissions", { requests: [] })
+ const ruleStore = state.useStorageSlot<RuleSlot>("rules", { rules: [] })
+
+ //init rules api
+ const ruleSet = useRuleSet(ruleStore)
+ const permissions = usePermissions(reqStore, ruleSet)
-})() \ No newline at end of file
+ return {
+ waitForChange: waitForChangeFn([currentConfig, loggedIn, reqStore, ruleStore]),
+
+ getRequests: () => Promise.resolve(permissions.getAll()),
+
+ deny(requestId: string) {
+ permissions.deny(requestId)
+ return Promise.resolve()
+ },
+
+ allow(requestId: string, addRule: boolean) {
+ permissions.allow(requestId, addRule)
+ return Promise.resolve()
+ },
+
+ clearRequests: optionsOnly(() => {
+ //clear stored requests
+ permissions.clearAll()
+ return Promise.resolve()
+ }),
+
+ async requestAndWaitResult(request: Partial<PermissionRequest>) {
+ //push request
+ const req = permissions.pushRequest(request, true)
+
+ //See if pending
+ if(req.status !== PrStatus.Pending){
+ //completed already, return status
+ return req.status
+ }
+
+ do {
+
+ //wait for a change
+ await waitOne([reqStore])
+
+ //check if request was approved
+ const status = permissions.getRequest(req.uuid);
+
+ switch(status?.status){
+ case PrStatus.Approved:
+ return PrStatus.Approved;
+ case PrStatus.Denied:
+ return PrStatus.Denied;
+ case PrStatus.Pending:
+ //continue waiting
+ break;
+ default:
+ throw new Error("Request was rejected or deleted")
+ }
+
+ //continue to wait for pending status
+ } while(true)
+ },
+
+ getRules: () => Promise.resolve(ruleSet.getRules()),
+
+ deleteRule: optionsOnly((rule: AutoAllowRule) => {
+ ruleSet.deleteRule(rule)
+ return Promise.resolve()
+ }),
+
+ addRule: optionsOnly((rule: AutoAllowRule) => {
+ ruleSet.addRule(rule)
+ return Promise.resolve()
+ }),
+ }
+ },
+ foreground: exportForegroundApi<PermissionApi>([
+ 'waitForChange',
+ 'getRequests',
+ 'clearRequests',
+ 'requestAndWaitResult',
+ 'getRules',
+ 'deleteRule',
+ 'addRule',
+ 'allow',
+ 'deny'
+ ]),
+ }
+} \ No newline at end of file
diff --git a/extension/src/features/pki-api.ts b/extension/src/features/pki-api.ts
index 41fbd48..07fb3df 100644
--- a/extension/src/features/pki-api.ts
+++ b/extension/src/features/pki-api.ts
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/extension/src/features/server-api/endpoints.ts b/extension/src/features/server-api/endpoints.ts
index 9c73866..5fa1bf4 100644
--- a/extension/src/features/server-api/endpoints.ts
+++ b/extension/src/features/server-api/endpoints.ts
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/extension/src/features/server-api/index.ts b/extension/src/features/server-api/index.ts
index cd67242..b9524ed 100644
--- a/extension/src/features/server-api/index.ts
+++ b/extension/src/features/server-api/index.ts
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/extension/src/features/settings.ts b/extension/src/features/settings.ts
index 9a3c32d..ca714a5 100644
--- a/extension/src/features/settings.ts
+++ b/extension/src/features/settings.ts
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/extension/src/features/tagfilter-api.ts b/extension/src/features/tagfilter-api.ts
index f5f1b6c..22369d0 100644
--- a/extension/src/features/tagfilter-api.ts
+++ b/extension/src/features/tagfilter-api.ts
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/extension/src/features/types.ts b/extension/src/features/types.ts
index 92cf6cf..fe59011 100644
--- a/extension/src/features/types.ts
+++ b/extension/src/features/types.ts
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/extension/src/features/util.ts b/extension/src/features/util.ts
index e9147bc..6ec8f15 100644
--- a/extension/src/features/util.ts
+++ b/extension/src/features/util.ts
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
@@ -20,15 +20,19 @@ import { type MaybeRefOrGetter, type WatchSource, isProxy, toRaw } from "vue";
import type { Watchable } from "./types";
export const waitForChange = <T extends Readonly<WatchSource<unknown>[]>>(source: [...T]):Promise<void> => {
- return new Promise((resolve) => watchOnce(source, () => resolve()))
+ return new Promise((resolve) => watchOnce<any>(source, () => resolve(), { deep: true }))
}
export const waitForChangeFn = <T extends Readonly<WatchSource<unknown>[]>>(source: [...T]) => {
return (): Promise<void> => {
- return new Promise((resolve) => watchOnce(source, () => resolve()))
+ return new Promise((resolve) => watchOnce<any>(source, () => resolve(), {deep: true}))
}
}
+export const waitOne = <T extends Readonly<WatchSource<unknown>[]>>(source: [...T]): Promise<void> => {
+ return new Promise((resolve) => watchOnce<any>(source, () => resolve(), { deep: true }))
+}
+
export const useStorage = <T>(storage: any & chrome.storage.StorageArea, key: string, initialValue: MaybeRefOrGetter<T>): RemovableRef<T> => {
const wrapper: StorageLikeAsync = {
diff --git a/extension/src/manifest.js b/extension/src/manifest.js
index f4c38fc..21c56d0 100644
--- a/extension/src/manifest.js
+++ b/extension/src/manifest.js
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Vaughn Nugent
+// Copyright (C) 2024 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
diff --git a/extension/vite.config.js b/extension/vite.config.js
index d28c0f7..35beb26 100644
--- a/extension/vite.config.js
+++ b/extension/vite.config.js
@@ -43,6 +43,9 @@ export default defineConfig(({ mode }) => {
scripts: [
'src/entries/nostr-provider.js', // defaults to webAccessible: true
],
+ html: [
+ 'src/entries/contentScript/auth-popup.html',
+ ]
},
}),
],