fea: nicer permissions
This commit is contained in:
@@ -1,9 +1,43 @@
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Literal
|
||||
|
||||
from pydantic import BaseModel, Field, root_validator
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ExtensionAPIMethodExport:
|
||||
method_id: str
|
||||
namespace: str
|
||||
name: str
|
||||
host_interface: str
|
||||
host_name: str
|
||||
sdk_name: str
|
||||
description: str
|
||||
required_permission: str | None = None
|
||||
require_auth: bool = True
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ExtensionAPIMethod:
|
||||
method_id: str
|
||||
namespace: str
|
||||
name: str
|
||||
python_name: str
|
||||
host_interface: str
|
||||
host_name: str
|
||||
sdk_name: str
|
||||
description: str
|
||||
request_model: type[BaseModel]
|
||||
response_model: type[BaseModel]
|
||||
required_permission: str | None = None
|
||||
require_auth: bool = True
|
||||
|
||||
@property
|
||||
def sdk_qualified_name(self) -> str:
|
||||
return f"{self.namespace}.{self.sdk_name}"
|
||||
|
||||
|
||||
class EmptyRequest(BaseModel):
|
||||
pass
|
||||
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -522,6 +522,17 @@ window.localisation.en = {
|
||||
extension_permissions_title: 'Grant extension permissions',
|
||||
extension_permissions_request: 'This extension requests these permissions:',
|
||||
extension_permissions_grant_install: 'Grant and install',
|
||||
extension_permissions_high_risk_warning:
|
||||
'This extension requests permissions that can move funds.',
|
||||
extension_permission_risk_low: 'Low risk',
|
||||
extension_permission_risk_medium: 'Medium risk',
|
||||
extension_permission_risk_high: 'High risk',
|
||||
extension_permission_warning_wallet_pay_invoice:
|
||||
'Can spend funds from wallets available to your account.',
|
||||
extension_permission_warning_extension_api_request_write:
|
||||
'Can write data or trigger actions in approved extensions.',
|
||||
extension_permission_warning_http_request:
|
||||
'Can send data to external services.',
|
||||
extension_permission_ext_storage_read: 'Read extension storage',
|
||||
extension_permission_ext_storage_read_public: 'Read public extension storage',
|
||||
extension_permission_ext_storage_write: 'Write extension storage',
|
||||
|
||||
@@ -724,6 +724,11 @@ window.PageExtensions = {
|
||||
resolve(grantedPermissions)
|
||||
}
|
||||
},
|
||||
permissionGrantHasHighRisk() {
|
||||
return this.permissionGrantDisplayItems().some(
|
||||
permission => permission.risk.level === 'high'
|
||||
)
|
||||
},
|
||||
permissionGrantDisplayItems() {
|
||||
const permissions = this.permissionGrant.permissions || []
|
||||
const permissionsById = new Map(
|
||||
@@ -781,6 +786,7 @@ window.PageExtensions = {
|
||||
label: isReadWriteStorage
|
||||
? this.$t('extension_permission_ext_storage_read_write')
|
||||
: this.permissionLabel(permission),
|
||||
risk: this.permissionRisk(permissions),
|
||||
badges: [],
|
||||
descriptions,
|
||||
fieldGroups: [],
|
||||
@@ -805,6 +811,68 @@ window.PageExtensions = {
|
||||
|
||||
return item
|
||||
},
|
||||
permissionRisk(permissions) {
|
||||
const risks = permissions.map(permission =>
|
||||
this.permissionRiskForPermission(permission)
|
||||
)
|
||||
const highestRisk = risks.find(risk => risk.level === 'high')
|
||||
if (highestRisk) return highestRisk
|
||||
return risks.find(risk => risk.level === 'medium') || this.lowRisk()
|
||||
},
|
||||
permissionRiskForPermission(permission) {
|
||||
if (permission.id === 'wallet.pay_invoice') {
|
||||
return this.highRisk('extension_permission_warning_wallet_pay_invoice')
|
||||
}
|
||||
if (permission.id === 'extension.api.request') {
|
||||
const hasWriteAccess = this.extensionApiPermissionTargets(
|
||||
permission
|
||||
).some(target => target.access.includes('write'))
|
||||
return hasWriteAccess
|
||||
? this.highRisk(
|
||||
'extension_permission_warning_extension_api_request_write'
|
||||
)
|
||||
: this.mediumRisk()
|
||||
}
|
||||
if (permission.id === 'http.request') {
|
||||
return this.highRisk('extension_permission_warning_http_request')
|
||||
}
|
||||
if (
|
||||
[
|
||||
'wallet.list',
|
||||
'wallet.balance.read',
|
||||
'wallet.create_invoice_public',
|
||||
'ext.storage.read_public',
|
||||
'payments.watch'
|
||||
].includes(permission.id)
|
||||
) {
|
||||
return this.mediumRisk()
|
||||
}
|
||||
return this.lowRisk()
|
||||
},
|
||||
lowRisk() {
|
||||
return {
|
||||
level: 'low',
|
||||
color: 'grey-6',
|
||||
label: this.$t('extension_permission_risk_low'),
|
||||
warning: ''
|
||||
}
|
||||
},
|
||||
mediumRisk() {
|
||||
return {
|
||||
level: 'medium',
|
||||
color: 'warning',
|
||||
label: this.$t('extension_permission_risk_medium'),
|
||||
warning: ''
|
||||
}
|
||||
},
|
||||
highRisk(warningKey) {
|
||||
return {
|
||||
level: 'high',
|
||||
color: 'negative',
|
||||
label: this.$t('extension_permission_risk_high'),
|
||||
warning: this.$t(warningKey)
|
||||
}
|
||||
},
|
||||
permissionOrderIndex(permissionId) {
|
||||
const order = [
|
||||
'wallet.pay_invoice',
|
||||
@@ -815,8 +883,8 @@ window.PageExtensions = {
|
||||
'ext.storage.read',
|
||||
'ext.storage.write',
|
||||
'ext.storage.read_public',
|
||||
'wallet.create_invoice',
|
||||
'wallet.create_invoice_public',
|
||||
'wallet.create_invoice',
|
||||
'utils.basic'
|
||||
]
|
||||
const index = order.indexOf(permissionId)
|
||||
|
||||
@@ -467,6 +467,16 @@
|
||||
class="text-body2 q-mt-sm"
|
||||
v-text="$t('extension_permissions_request')"
|
||||
></div>
|
||||
<q-banner
|
||||
v-if="permissionGrantHasHighRisk()"
|
||||
dense
|
||||
class="bg-red-1 text-red-10 q-mt-md"
|
||||
>
|
||||
<template v-slot:avatar>
|
||||
<q-icon name="warning" color="negative"></q-icon>
|
||||
</template>
|
||||
<span v-text="$t('extension_permissions_high_risk_warning')"></span>
|
||||
</q-banner>
|
||||
</q-card-section>
|
||||
|
||||
<q-list bordered separator class="q-mt-md">
|
||||
@@ -475,27 +485,44 @@
|
||||
:key="permission.id"
|
||||
dense
|
||||
expand-separator
|
||||
class="q-pt-xs"
|
||||
>
|
||||
<template v-slot:header>
|
||||
<q-item-section>
|
||||
<q-item-label
|
||||
class="text-weight-medium"
|
||||
v-text="permission.label"
|
||||
></q-item-label>
|
||||
<q-item-label v-if="permission.badges.length" caption>
|
||||
<q-item-label class="text-weight-medium">
|
||||
<span v-text="permission.label"></span>
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
<q-item-section
|
||||
v-if="permission.risk.level !== 'low' || permission.badges.length"
|
||||
side
|
||||
top
|
||||
>
|
||||
<div class="row items-center justify-end q-gutter-xs">
|
||||
<q-badge
|
||||
v-for="badge of permission.badges"
|
||||
:key="badge.key"
|
||||
outline
|
||||
color="primary"
|
||||
class="q-mr-xs q-mt-xs"
|
||||
v-text="badge.label"
|
||||
></q-badge>
|
||||
</q-item-label>
|
||||
<q-badge
|
||||
v-if="permission.risk.level !== 'low'"
|
||||
:color="permission.risk.color"
|
||||
v-text="permission.risk.label"
|
||||
></q-badge>
|
||||
</div>
|
||||
</q-item-section>
|
||||
</template>
|
||||
|
||||
<div class="q-px-md q-pb-sm">
|
||||
<div
|
||||
v-if="permission.risk.warning"
|
||||
class="row items-center text-negative text-caption q-mb-xs"
|
||||
>
|
||||
<q-icon name="warning" size="16px" class="q-mr-xs"></q-icon>
|
||||
<span v-text="permission.risk.warning"></span>
|
||||
</div>
|
||||
<p
|
||||
v-for="description of permission.descriptions"
|
||||
:key="description"
|
||||
|
||||
Reference in New Issue
Block a user