Skip to content

Commit

Permalink
feat: permission presets (#3103)
Browse files Browse the repository at this point in the history
* feat: permission presets

* feat: preset translations + preset types

* fix: translations

* fix: refactor presets

* fix: renames

* fix: layout

* fix: reasonable permisison defaults

* fix: translation

* fix: add edit follow list

---------

Co-authored-by: René Aaron <[email protected]>
Co-authored-by: René Aaron <[email protected]>
  • Loading branch information
3 people authored Mar 25, 2024
1 parent 6379196 commit 6b45f0d
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 27 deletions.
114 changes: 96 additions & 18 deletions src/app/components/Enable/NostrEnable.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
import ConfirmOrCancel from "@components/ConfirmOrCancel";
import Container from "@components/Container";
import PublisherCard from "@components/PublisherCard";
import { PopiconsCheckLine } from "@popicons/react";
import {
PopiconsCheckLine,
PopiconsGlassesSolid,
PopiconsHeartLine,
PopiconsLikeLine,
} from "@popicons/react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import Alert from "~/app/components/Alert";
import ScreenHeader from "~/app/components/ScreenHeader";
import toast from "~/app/components/Toast";
import { classNames } from "~/app/utils";
import { USER_REJECTED_ERROR } from "~/common/constants";
import msg from "~/common/lib/msg";
import type { OriginData } from "~/types";
import { NostrPermissionPreset, type OriginData } from "~/types";

type Props = {
origin: OriginData;
};
function NostrEnableComponent(props: Props) {
const [loading, setLoading] = useState(false);
const [selectedPreset, setSelectedPreset] = useState(
NostrPermissionPreset.REASONABLE
);
const hasHttp = props.origin.domain.startsWith("http://");
const { t } = useTranslation("translation", {
keyPrefix: "nostr_enable",
Expand All @@ -25,9 +34,11 @@ function NostrEnableComponent(props: Props) {
const enable = () => {
try {
setLoading(true);

msg.reply({
enabled: true,
remember: true,
preset: selectedPreset,
});
} catch (e) {
console.error(e);
Expand Down Expand Up @@ -61,28 +72,43 @@ function NostrEnableComponent(props: Props) {
title={props.origin.name}
image={props.origin.icon}
url={props.origin.host}
isSmall={false}
/>

<div className="pt-3">
{hasHttp && (
{hasHttp && (
<div className="pt-3 text-sm">
<Alert type="warn">
{tCommon("enable.insecure_domain_warn")}
</Alert>
)}
</div>

<div className="dark:text-white pt-6">
<p className="mb-2">{tCommon("enable.allow")}</p>

<div className="mb-2 flex items-center">
<PopiconsCheckLine className="w-5 h-5 mr-2" />
<p className="dark:text-white">{t("request1")}</p>
</div>
<div className="mb-2 flex items-center">
<PopiconsCheckLine className="w-5 h-5 mr-2" />
<p className="dark:text-white">{t("request2")}</p>
</div>
)}

<div className="flex flex-col gap-2 dark:text-white my-5">
<p className="text-base font-medium">{t("description")}</p>
<PermissionPreset
title={t("presets.trust_fully.title")}
description={t("presets.trust_fully.description")}
icon={<PopiconsHeartLine className="w-6 h-6" />}
onClick={() =>
setSelectedPreset(NostrPermissionPreset.TRUST_FULLY)
}
isSelected={selectedPreset === NostrPermissionPreset.TRUST_FULLY}
/>
<PermissionPreset
title={t("presets.reasonable.title")}
description={t("presets.reasonable.description")}
icon={<PopiconsLikeLine className="w-6 h-6" />}
onClick={() =>
setSelectedPreset(NostrPermissionPreset.REASONABLE)
}
isSelected={selectedPreset === NostrPermissionPreset.REASONABLE}
/>
<PermissionPreset
title={t("presets.paranoid.title")}
description={t("presets.paranoid.description")}
icon={<PopiconsGlassesSolid className="w-6 h-6" />}
onClick={() => setSelectedPreset(NostrPermissionPreset.PARANOID)}
isSelected={selectedPreset === NostrPermissionPreset.PARANOID}
/>
</div>
</div>
<div className="text-center flex flex-col">
Expand All @@ -106,4 +132,56 @@ function NostrEnableComponent(props: Props) {
);
}

type PermissionPresetProps = {
title: string;
description: string;
icon: React.ReactNode;
onClick: () => void;
isSelected: boolean;
};
function PermissionPreset({
icon,
title,
description,
onClick,
isSelected,
}: PermissionPresetProps) {
return (
<button
className={classNames(
"text-left border border-gray-200 dark:border-neutral-800 rounded-xl p-4 text-gray-800 dark:text-neutral-200 cursor-pointer flex flex-row items-center gap-3",
isSelected
? "bg-amber-50 dark:bg-surface-02dp ring-primary border-primary dark:border-primary ring-1"
: "bg-white dark:bg-surface-01dp hover:bg-gray-50 dark:hover:bg-surface-02dp"
)}
onClick={onClick}
>
<div
className={classNames(
"flex-shrink-0 flex justify-center md:px-3",
isSelected ? "text-amber-400" : "text-gray-400 dark:text-neutral-600"
)}
>
{icon}
</div>
<div className="flex-grow space-y-0.5">
<div className="font-medium leading-5 text-sm md:text-base">
{title}
</div>
<div className="text-gray-600 dark:text-neutral-400 text-xs leading-4 md:text-sm">
{description}
</div>
</div>
<div
className={classNames(
"flex-shrink-0 flex justify-end text-gray-400 dark:text-neutral-600",
isSelected ? "" : "hidden"
)}
>
<PopiconsCheckLine className="w-5" />
</div>
</button>
);
}

export default NostrEnableComponent;
4 changes: 2 additions & 2 deletions src/app/components/SitePreferences/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ function SitePreferences({ launcherType, allowance, onEdit, onDelete }: Props) {
label={
permission.method
.toLowerCase()
.startsWith("nostr/signmessage")
.startsWith("nostr/signmessage/")
? tNostr(
`kinds.${getPermissionKind(
permission
Expand All @@ -280,7 +280,7 @@ function SitePreferences({ launcherType, allowance, onEdit, onDelete }: Props) {
description={
permission.method
.toLowerCase()
.startsWith("nostr/signmessage")
.startsWith("nostr/signmessage/")
? tNostr(
`kinds.${getPermissionKind(
permission
Expand Down
48 changes: 46 additions & 2 deletions src/extension/background-script/actions/nostr/enable.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import utils from "~/common/lib/utils";
import { getHostFromSender } from "~/common/utils/helpers";
import db from "~/extension/background-script/db";
import type { MessageAllowanceEnable, Sender } from "~/types";
import {
NostrPermissionPreset,
PermissionMethodNostr,
type MessageAllowanceEnable,
type Sender,
} from "~/types";

import { addPermissionFor } from "~/extension/background-script/permissions";
import state from "../../state";
import { ExtensionIcon, setIcon } from "../setup/setIcon";

Expand All @@ -13,7 +19,6 @@ const enable = async (message: MessageAllowanceEnable, sender: Sender) => {
const isUnlocked = await state.getState().isUnlocked();
const account = await state.getState().getAccount();
const allowance = await db.allowances

.where("host")
.equalsIgnoreCase(host)
.first();
Expand All @@ -35,6 +40,7 @@ const enable = async (message: MessageAllowanceEnable, sender: Sender) => {
const response = await utils.openPrompt<{
enabled: boolean;
remember: boolean;
preset: string;
}>(message);

if (response.data.enabled && sender.tab) {
Expand Down Expand Up @@ -72,6 +78,44 @@ const enable = async (message: MessageAllowanceEnable, sender: Sender) => {
tag: "",
});
}
if (response.data.preset === NostrPermissionPreset.REASONABLE) {
// Add permissions
const permissions: PermissionMethodNostr[] = [
PermissionMethodNostr.NOSTR_GETPUBLICKEY,
PermissionMethodNostr.NOSTR_NIP04ENCRYPT,
PermissionMethodNostr.NOSTR_NIP04DECRYPT,
PermissionMethodNostr.NOSTR_NIP44ENCRYPT,
PermissionMethodNostr.NOSTR_NIP44DECRYPT,
];
permissions.forEach(async (permission) => {
await addPermissionFor(permission, host);
});

// Add specific signing permissions
const reasonableEventKindIds = [
0, // Update profile
1, // Short text note
3, // Update follow list
4, // Encrypted direct messages
7, // Reaction
9734, // Zap request
10002, // Relay list metadata
22242, // Client relay authentication
30023, // Long-form content
30008, // Manage profile badges
30009, // Badge definition
];
reasonableEventKindIds.forEach(async (kindId) => {
await addPermissionFor(
PermissionMethodNostr.NOSTR_SIGNMESSAGE + "/" + kindId,
host
);
});
} else if (response.data.preset === NostrPermissionPreset.TRUST_FULLY) {
Object.values(PermissionMethodNostr).forEach(async (permission) => {
await addPermissionFor(permission, host);
});
}
await db.saveToStorage();
}
return {
Expand Down
23 changes: 18 additions & 5 deletions src/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -552,8 +552,21 @@
},
"nostr_enable": {
"title": "Connect to Nostr",
"request1": "Request to read your Nostr public key",
"request2": "Request to sign events using your private key"
"description": "How should this app's requests be handled?",
"presets": {
"trust_fully": {
"title": "I fully trust it",
"description": "Auto-sign all requests (except payments)"
},
"reasonable": {
"title": "Let's be reasonable",
"description": "Auto-approve most common requests"
},
"paranoid": {
"title": "I'm a bit paranoid",
"description": "Do not sign anything without asking me!"
}
}
},
"liquid_enable": {
"title": "Connect to Liquid",
Expand Down Expand Up @@ -980,7 +993,7 @@
},
"1984": {
"title": "report note",
"description": "Report a note for spam, illegal or explicit content "
"description": "Report a note for spam, illegal or explicit content"
},
"9734": {
"title": "request zap",
Expand Down Expand Up @@ -1012,11 +1025,11 @@
},
"30023": {
"title": "long note",
"description": "Sign a long-form notes like articles or blog posts"
"description": "Sign long-form notes like articles or blog posts"
},
"30078": {
"title": "app data",
"description": "Sign an application-specific data"
"description": "Sign application-specific data"
}
}
},
Expand Down
6 changes: 6 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,12 @@ export interface Payment extends Omit<DbPayment, "id"> {
id: number;
}

export enum NostrPermissionPreset {
TRUST_FULLY = "trust_fully",
REASONABLE = "reasonable",
PARANOID = "paranoid",
}

export enum PermissionOption {
ASK_EVERYTIME = "ask_everytime",
DONT_ASK_CURRENT = "dont_ask_current",
Expand Down

0 comments on commit 6b45f0d

Please sign in to comment.