Change perm sets, add 'basic' perm, fix disallow logic
This commit is contained in:
27
src/App.tsx
27
src/App.tsx
@@ -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])
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
@@ -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') {
|
||||
|
@@ -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 {
|
||||
|
@@ -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',
|
||||
}
|
@@ -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
|
||||
}
|
||||
|
Reference in New Issue
Block a user