Change perm sets, add 'basic' perm, fix disallow logic

This commit is contained in:
artur
2024-01-29 11:33:04 +03:00
parent 5fa22a2d9e
commit 0044697159
7 changed files with 167 additions and 66 deletions

View File

@@ -28,18 +28,24 @@ function App() {
const keys: DbKey[] = await dbi.listKeys()
console.log(keys, 'keys')
const newKeys = []
dispatch(setKeys({ keys }))
const loadProfiles = async () => {
const newKeys = []
for (const key of keys) {
const response = await fetchProfile(key.npub)
if (!response) {
newKeys.push(key)
} else {
newKeys.push({ ...key, profile: response })
for (const key of keys) {
// make it async
const response = await fetchProfile(key.npub)
if (!response) {
newKeys.push(key)
} else {
newKeys.push({ ...key, profile: response })
}
}
}
dispatch(setKeys({ keys: newKeys }))
dispatch(setKeys({ keys: newKeys }))
}
// async load to avoid blocking main code below
loadProfiles()
const apps = await dbi.listApps()
dispatch(
@@ -61,6 +67,7 @@ function App() {
if (firstPending.get(p.appNpub)) continue
firstPending.set(p.appNpub, p)
}
// console.log({ pending, firstPending })
// @ts-ignore
dispatch(setPending({ pending: [...firstPending.values()] }))
@@ -68,7 +75,7 @@ function App() {
// rerender
// setRender((r) => r + 1)
if (!newKeys.length)
if (!keys.length)
handleOpen(MODAL_PARAMS_KEYS.INITIAL)
}, [dispatch])

View File

@@ -10,12 +10,8 @@ import { StyledButton, StyledToggleButtonsGroup } from './styled'
import { ActionToggleButton } from './сomponents/ActionToggleButton'
import { useState } from 'react'
import { swicCall } from '@/modules/swic'
import { ACTION_TYPE } from '@/utils/consts'
enum ACTION_TYPE {
BASIC = 'Basic',
ADVANCED = 'Advanced',
CUSTOM = 'Custom',
}
export const ModalConfirmConnect = () => {
const { getModalOpened, handleClose } = useModalSearchParams()
@@ -61,14 +57,22 @@ export const ModalConfirmConnect = () => {
id: string,
allow: boolean,
remember: boolean,
options?: any
) {
call(async () => {
await swicCall('confirm', id, allow, remember)
console.log('confirmed', id, allow, remember)
await swicCall('confirm', id, allow, remember, options)
console.log('confirmed', id, allow, remember, options)
closeModalAfterRequest()
})
}
const allow = () => {
const options: any = {};
if (selectedActionType === ACTION_TYPE.BASIC)
options.perm = ACTION_TYPE.BASIC;
confirmPending(pendingReqId, true, true, options)
}
return (
<Modal open={isModalOpened} onClose={handleCloseModal}>
<Stack gap={'1rem'} paddingTop={'1rem'}>
@@ -102,34 +106,35 @@ export const ModalConfirmConnect = () => {
>
<ActionToggleButton
value={ACTION_TYPE.BASIC}
title='Basic'
description='Use this for most apps'
hasinfo
title='Basic permissions'
description='Read your public key, sign notes and reactions'
// hasinfo
/>
<ActionToggleButton
{/* <ActionToggleButton
value={ACTION_TYPE.ADVANCED}
title='Advanced'
description='Use for trusted apps only'
hasinfo
/>
/> */}
<ActionToggleButton
value={ACTION_TYPE.CUSTOM}
title='Custom'
description='Gives you full control'
title='On demand'
description='Assign permissions when the app asks for them'
/>
</StyledToggleButtonsGroup>
<Stack direction={'row'} gap={'1rem'}>
<StyledButton
onClick={handleCloseModal}
onClick={() => confirmPending(pendingReqId, false, true)}
varianttype='secondary'
>
Cancel
Disallow
</StyledButton>
<StyledButton
fullWidth
onClick={() => confirmPending(pendingReqId, true, true)}
onClick={allow}
>
Allow {selectedActionType} actions
{/* Allow {selectedActionType} actions */}
Connect
</StyledButton>
</Stack>
</Stack>

View File

@@ -1,7 +1,7 @@
import { useModalSearchParams } from '@/hooks/useModalSearchParams'
import { Modal } from '@/shared/Modal/Modal'
import { MODAL_PARAMS_KEYS } from '@/types/modal'
import { call, getShortenNpub } from '@/utils/helpers'
import { call, getShortenNpub, getSignReqKind } from '@/utils/helpers'
import {
Avatar,
Box,
@@ -47,6 +47,8 @@ type ModalConfirmEventProps = {
export const ACTIONS: { [type: string]: string } = {
get_public_key: 'Get public key',
sign_event: 'Sign event',
nip04_encrypt: 'Encrypt message',
nip04_decrypt: 'Decrypt message',
}
type PendingRequest = DbPending & { checked: boolean }
@@ -109,12 +111,12 @@ export const ModalConfirmEvent: FC<ModalConfirmEventProps> = ({
},
)
async function confirmPending() {
async function confirmPending(allow: boolean) {
selectedPendingRequests.forEach((req) => {
call(async () => {
const remember = selectedActionType !== ACTION_TYPE.ONCE
await swicCall('confirm', req.id, true, remember)
console.log('confirmed', req.id, selectedActionType)
await swicCall('confirm', req.id, allow, remember)
console.log('confirmed', req.id, selectedActionType, allow)
})
})
closeModalAfterRequest()
@@ -128,6 +130,16 @@ export const ModalConfirmEvent: FC<ModalConfirmEventProps> = ({
setPendingRequests(newPendingRequests)
}
const getAction = (req: PendingRequest) => {
const action = ACTIONS[req.method]
if (req.method === 'sign_event') {
const kind = getSignReqKind(req)
if (kind !== undefined)
return `${action} of kind ${kind}`
}
return action
}
return (
<Modal open={isModalOpened} onClose={handleCloseModal}>
<Stack gap={'1rem'} paddingTop={'1rem'}>
@@ -171,7 +183,7 @@ export const ModalConfirmEvent: FC<ModalConfirmEventProps> = ({
/>
</ListItemIcon>
<ListItemText>
{ACTIONS[req.method]}
{getAction(req)}
</ListItemText>
</ListItem>
)
@@ -191,21 +203,21 @@ export const ModalConfirmEvent: FC<ModalConfirmEventProps> = ({
value={ACTION_TYPE.ONCE}
title='Just once'
/>
<ActionToggleButton
{/* <ActionToggleButton
value={ACTION_TYPE.ALLOW_ALL}
title='Allow All Advanced Actions'
hasinfo
/>
/> */}
</StyledToggleButtonsGroup>
<Stack direction={'row'} gap={'1rem'}>
<StyledButton
onClick={handleCloseModal}
onClick={() => confirmPending(false)}
varianttype='secondary'
>
Cancel
Disallow {ACTION_LABELS[selectedActionType]}
</StyledButton>
<StyledButton fullWidth onClick={confirmPending}>
<StyledButton onClick={() => confirmPending(true)}>
Allow {ACTION_LABELS[selectedActionType]}
</StyledButton>
</Stack>

View File

@@ -10,6 +10,7 @@ import NDK, {
} from '@nostr-dev-kit/ndk'
import { NOAUTHD_URL, WEB_PUSH_PUBKEY, NIP46_RELAYS } from '../utils/consts'
import { Nip04 } from './nip04'
import { getReqPerm, isPackagePerm } from '@/utils/helpers'
//import { PrivateKeySigner } from './signer'
//const PERF_TEST = false
@@ -30,7 +31,7 @@ interface Key {
interface Pending {
req: DbPending
cb: (allow: boolean, remember: boolean) => void
cb: (allow: boolean, remember: boolean, options?: any) => void
}
interface IAllowCallbackParams {
@@ -138,6 +139,7 @@ class EventHandlingStrategyWrapper implements IEventHandlingStrategy {
}
}
export class NoauthBackend {
readonly swg: ServiceWorkerGlobalScope
private keysModule: Keys
@@ -438,14 +440,21 @@ export class NoauthBackend {
}
private getPerm(req: DbPending): string {
return (
this.perms.find(
(p) =>
p.npub === req.npub &&
p.appNpub === req.appNpub &&
p.perm === req.method,
)?.value || ''
const perm = getReqPerm(req)
const appPerms = this.perms.filter(
(p) =>
p.npub === req.npub &&
p.appNpub === req.appNpub
)
// exact match first
let p = appPerms.find((p) => p.perm === perm)
// non-exact next
if (!p)
p = appPerms.find((p) => isPackagePerm(p.perm, perm))
console.log("req", req, "perm", perm, "value", p);
return p?.value || ''
}
private async allowPermitCallback({
@@ -479,6 +488,7 @@ export class NoauthBackend {
manual: boolean,
allow: boolean,
remember: boolean,
options?: any
) => {
// confirm
console.log(
@@ -486,20 +496,24 @@ export class NoauthBackend {
allow ? 'allowed' : 'disallowed',
npub,
method,
options,
params,
)
if (manual) {
await dbi.confirmPending(id, allow)
if (!(await dbi.getApp(req.appNpub))) {
await dbi.addApp({
appNpub: req.appNpub,
npub: req.npub,
timestamp: Date.now(),
name: '',
icon: '',
url: '',
})
if (!(method === 'connect' && !allow)) {
// only add app if it's not 'disallow connect'
if (!(await dbi.getApp(req.appNpub))) {
await dbi.addApp({
appNpub: req.appNpub,
npub: req.npub,
timestamp: Date.now(),
name: '',
icon: '',
url: '',
})
}
}
} else {
// just send to db w/o waiting for it
@@ -520,11 +534,16 @@ export class NoauthBackend {
if (index >= 0) self.confirmBuffer.splice(index, 1)
if (remember) {
let perm = getReqPerm(req)
if (allow && options && options.perm)
perm = options.perm
await dbi.addPerm({
id: req.id,
npub: req.npub,
appNpub: req.appNpub,
perm: method,
perm,
value: allow ? '1' : '0',
timestamp: Date.now(),
})
@@ -534,9 +553,12 @@ export class NoauthBackend {
const otherReqs = self.confirmBuffer.filter(
(r) => r.req.appNpub === req.appNpub,
)
console.log("updated perms", this.perms, "otherReqs", otherReqs)
for (const r of otherReqs) {
if (r.req.method === req.method) {
r.cb(allow, false)
const perm = this.getPerm(r.req);
// if (r.req.method === req.method) {
if (perm) {
r.cb(perm === '1', false)
}
}
}
@@ -567,7 +589,7 @@ export class NoauthBackend {
// put to a list of pending requests
this.confirmBuffer.push({
req,
cb: (allow, remember) => onAllow(true, allow, remember),
cb: (allow, remember, options) => onAllow(true, allow, remember, options),
})
// show notifs
@@ -730,7 +752,7 @@ export class NoauthBackend {
return k
}
private async confirm(id: string, allow: boolean, remember: boolean) {
private async confirm(id: string, allow: boolean, remember: boolean, options?: any) {
const req = this.confirmBuffer.find((r) => r.req.id === id)
if (!req) {
console.log('req ', id, 'not found')
@@ -738,8 +760,8 @@ export class NoauthBackend {
await dbi.removePending(id)
this.updateUI()
} else {
console.log('confirming', id, allow, remember)
req.cb(allow, remember)
console.log('confirming req', id, allow, remember, options)
req.cb(allow, remember, options)
}
}
@@ -794,7 +816,7 @@ export class NoauthBackend {
} else if (method === 'fetchKey') {
result = await this.fetchKey(args[0], args[1])
} else if (method === 'confirm') {
result = await this.confirm(args[0], args[1], args[2])
result = await this.confirm(args[0], args[1], args[2], args[3])
} else if (method === 'deleteApp') {
result = await this.deleteApp(args[0])
} else if (method === 'deletePerm') {

View File

@@ -25,6 +25,7 @@ import { useEnqueueSnackbar } from '@/hooks/useEnqueueSnackbar'
import { ModalConfirmConnect } from '@/components/Modal/ModalConfirmConnect/ModalConfirmConnect'
import { ModalConfirmEvent } from '@/components/Modal/ModalConfirmEvent/ModalConfirmEvent'
import { DbPending } from '@/modules/db'
import { ACTION_TYPE } from '@/utils/consts'
export type IPendingsByAppNpub = {
[appNpub: string]: {
@@ -63,7 +64,8 @@ const KeyPage = () => {
const filteredPerms = perms.filter((p) => p.npub === npub)
const npubConnectPerms = filteredPerms.filter(
(perm) => perm.perm === 'connect',
(perm) => perm.perm === 'connect'
|| perm.perm === ACTION_TYPE.BASIC.toLowerCase(),
)
const excludeConnectPendings = filteredPendingReqs.filter(
(pr) => pr.method !== 'connect',
@@ -88,6 +90,14 @@ const KeyPage = () => {
acc[current.appNpub].isConnected = isConnected
return acc
}, {})
// console.log({
// pending,
// filteredPerms,
// npubConnectPerms,
// excludeConnectPendings,
// connectPendings,
// prepareEventPendings
// });
const load = useCallback(async () => {
try {

View File

@@ -1,3 +1,9 @@
export const NIP46_RELAYS = ['wss://relay.login.nostrapps.org']
export const NOAUTHD_URL = process.env.REACT_APP_NOAUTHD_URL
export const WEB_PUSH_PUBKEY = process.env.REACT_APP_WEB_PUSH_PUBKEY
export enum ACTION_TYPE {
BASIC = 'Basic',
ADVANCED = 'Advanced',
CUSTOM = 'Custom',
}

View File

@@ -1,5 +1,6 @@
import { nip19 } from 'nostr-tools'
import { NIP46_RELAYS } from './consts'
import { ACTION_TYPE, NIP46_RELAYS } from './consts'
import { DbPending } from '@/modules/db'
export async function log(s: string) {
const log = document.getElementById('log')
@@ -15,7 +16,7 @@ export async function call(cb: () => any) {
}
export const getShortenNpub = (npub = '') => {
return npub.substring(0, 10) + '...' + npub.slice(-6)
return npub.substring(0, 10) + '...' + npub.slice(-4)
}
export const getBunkerLink = (npub = '') => {
@@ -39,3 +40,41 @@ export async function askNotificationPermission() {
}
})
}
export function getSignReqKind(req: DbPending): number | undefined {
try {
const data = JSON.parse(JSON.parse(req.params)[0])
return data.kind
} catch {}
return undefined
}
export function getReqPerm(req: DbPending): string {
if (req.method === 'sign_event') {
const kind = getSignReqKind(req)
if (kind !== undefined)
return `${req.method}:${kind}`
}
return req.method
}
export function isPackagePerm(perm: string, reqPerm: string) {
if (perm === ACTION_TYPE.BASIC.toLowerCase()) {
switch (reqPerm) {
case 'get_public_key':
case 'nip04_decrypt':
case 'nip04_encrypt':
case 'sign_event:0':
case 'sign_event:1':
case 'sign_event:3':
case 'sign_event:6':
case 'sign_event:7':
case 'sign_event:9734':
case 'sign_event:10002':
case 'sign_event:30023':
case 'sign_event:10000':
return true
}
}
return false
}