ask permissions

fix_save
filip mertens 3 years ago
parent 9da8612e79
commit 02f2cec119
  1. 11
      apps/remix-ide/src/app/plugins/permission-handler-plugin.tsx
  2. 58
      apps/remix-ide/src/remixAppManager.js
  3. 4
      libs/remix-core-plugin/src/lib/compiler-content-imports.ts
  4. 3
      libs/remix-ui/permission-handler/src/interface/index.ts
  5. 3
      libs/remix-ui/permission-handler/src/lib/permission-dialog.tsx

@ -70,12 +70,12 @@ export class PermissionHandlerPlugin extends Plugin {
* @param {string} message from the caller plugin to add more details if needed
* @returns {Promise<boolean>}
*/
async askPermission(from: Profile, to: Profile, method: string, message: string) {
async askPermission(from: Profile, to: Profile, method: string, message: string, sensitiveCall: boolean) {
try {
this.permissions = this._getFromLocal()
if (!this.permissions[to.name]) this.permissions[to.name] = {}
if (!this.permissions[to.name][method]) this.permissions[to.name][method] = {}
if (!this.permissions[to.name][method][from.name]) return this.openPermission(from, to, method, message)
if (!this.permissions[to.name][method][from.name]) return this.openPermission(from, to, method, message, sensitiveCall)
const { allow, hash } = this.permissions[to.name][method][from.name]
if (!allow) {
@ -85,20 +85,21 @@ export class PermissionHandlerPlugin extends Plugin {
}
return hash === from.hash
? true // Allow
: await this.openPermission(from, to, method, message)
: await this.openPermission(from, to, method, message, sensitiveCall)
} catch (err) {
throw new Error(err)
}
}
async openPermission(from: Profile, to: Profile, method: string, message: string) {
async openPermission(from: Profile, to: Profile, method: string, message: string, sensitiveCall: boolean) {
const remember = this.permissions[to.name][method][from.name]
const value: PermissionHandlerValue = {
from,
to,
method,
message,
remember
remember,
sensitiveCall
}
const modal: AppModal = {
id: 'PermissionHandler',

@ -12,7 +12,12 @@ const requiredModules = [ // services + layout views + system views
const dependentModules = ['git', 'hardhat', 'truffle', 'slither'] // module which shouldn't be manually activated (e.g git is activated by remixd)
export function isNative (name) {
const sensitiveCalls = {
'fileManager': ['writeFile', 'copyFile', 'rename', 'copyDir'],
'contentImport': ['resolveAndSave']
}
export function isNative(name) {
const nativePlugins = ['vyper', 'workshops', 'debugger', 'remixd', 'menuicons', 'solidity', 'hardhat-provider', 'solidityStaticAnalysis', 'solidityUnitTesting', 'layout', 'notification', 'hardhat-provider', 'ganache-provider']
return nativePlugins.includes(name) || requiredModules.includes(name)
}
@ -27,34 +32,34 @@ export function isNative (name) {
* @param {any, any}
* @returns {boolean}
*/
export function canActivate (from, to) {
export function canActivate(from, to) {
return ['ethdoc'].includes(from.name) ||
isNative(from.name) ||
(to && from && from.canActivate && from.canActivate.includes(to.name))
isNative(from.name) ||
(to && from && from.canActivate && from.canActivate.includes(to.name))
}
export class RemixAppManager extends PluginManager {
constructor () {
constructor() {
super()
this.event = new EventEmitter()
this.pluginsDirectory = 'https://raw.githubusercontent.com/ethereum/remix-plugins-directory/master/build/metadata.json'
this.pluginLoader = new PluginLoader()
}
async canActivatePlugin (from, to) {
async canActivatePlugin(from, to) {
return canActivate(from, to)
}
async canDeactivatePlugin (from, to) {
async canDeactivatePlugin(from, to) {
if (requiredModules.includes(to.name)) return false
return isNative(from.name)
}
async canDeactivate(from,to) {
return this.canDeactivatePlugin(from, to)
async canDeactivate(from, to) {
return this.canDeactivatePlugin(from, to)
}
async deactivatePlugin (name) {
async deactivatePlugin(name) {
const [to, from] = [
await this.getProfile(name),
await this.getProfile(this.requestFrom)
@ -64,52 +69,55 @@ export class RemixAppManager extends PluginManager {
}
}
async canCall (from, to, method, message) {
async canCall(from, to, method, message) {
const isSensitiveCall = sensitiveCalls[to] && sensitiveCalls[to].includes(method)
// Make sure the caller of this methods is the target plugin
if (to !== this.currentRequest.from) {
return false
}
// skipping native plugins' requests
if (isNative(from)) {
if (isNative(from) && !isSensitiveCall) {
return true
}
// ask the user for permission
return await this.call('permissionhandler', 'askPermission', this.profiles[from], this.profiles[to], method, message)
return await this.call('permissionhandler', 'askPermission', this.profiles[from], this.profiles[to], method, message, isSensitiveCall)
}
onPluginActivated (plugin) {
onPluginActivated(plugin) {
this.pluginLoader.set(plugin, this.actives)
this.event.emit('activate', plugin)
this.emit('activate', plugin)
if (!requiredModules.includes(plugin.name)) _paq.push(['trackEvent', 'pluginManager', 'activate', plugin.name])
}
getAll () {
getAll() {
return Object.keys(this.profiles).map((p) => {
return this.profiles[p]
})
}
getIds () {
getIds() {
return Object.keys(this.profiles)
}
onPluginDeactivated (plugin) {
onPluginDeactivated(plugin) {
this.pluginLoader.set(plugin, this.actives)
this.event.emit('deactivate', plugin)
_paq.push(['trackEvent', 'pluginManager', 'deactivate', plugin.name])
}
isDependent (name) {
isDependent(name) {
return dependentModules.includes(name)
}
isRequired (name) {
isRequired(name) {
// excluding internal use plugins
return requiredModules.includes(name)
}
async registeredPlugins () {
async registeredPlugins() {
let plugins
try {
const res = await fetch(this.pluginsDirectory)
@ -138,7 +146,7 @@ export class RemixAppManager extends PluginManager {
})
}
async registerContextMenuItems () {
async registerContextMenuItems() {
await this.call('filePanel', 'registerContextMenuItem', {
id: 'flattener',
name: 'flattenFileCustomAction',
@ -167,11 +175,11 @@ export class RemixAppManager extends PluginManager {
* (localStorage, queryParams)
**/
class PluginLoader {
get currentLoader () {
get currentLoader() {
return this.loaders[this.current]
}
constructor () {
constructor() {
const queryParams = new QueryParams()
this.donotAutoReload = ['remixd', 'git'] // that would be a bad practice to force loading some plugins at page load.
this.loaders = {}
@ -195,11 +203,11 @@ class PluginLoader {
this.current = queryParams.get().activate ? 'queryParams' : 'localStorage'
}
set (plugin, actives) {
set(plugin, actives) {
this.currentLoader.set(plugin, actives)
}
get () {
get() {
return this.currentLoader.get()
}
}

@ -125,7 +125,7 @@ export class CompilerImports extends Plugin {
async resolveAndSave (url, targetPath) {
try {
if (targetPath && this.currentRequest) {
const canCall = await this.askUserPermission('writeFile', 'This action will update the path ' + targetPath)
const canCall = await this.askUserPermission('resolveAndSave', 'This action will update the path ' + targetPath)
if (!canCall) throw new Error('No permission to update ' + targetPath)
}
const provider = await this.call('fileManager', 'getProviderOf', url)
@ -181,7 +181,7 @@ export class CompilerImports extends Plugin {
}
}
} catch (e) {
throw new Error(`not found ${url}`)
throw new Error(e)
}
}
}

@ -5,7 +5,8 @@ export interface PermissionHandlerValue {
to: Profile,
remember: boolean,
method: string,
message: string
message: string,
sensitiveCall: boolean
}
export interface PermissionHandlerProps {

@ -3,7 +3,7 @@ import { PermissionHandlerProps } from '../interface'
import './permission-dialog.css'
const PermissionHandlerDialog = (props: PermissionHandlerProps) => {
const { from, to, remember, method, message } = props.value
const { from, to, remember, method, message, sensitiveCall } = props.value
const [feedback, setFeedback] = useState<string>('')
const theme = props.theme
@ -43,7 +43,6 @@ const PermissionHandlerDialog = (props: PermissionHandlerProps) => {
</div> : null
}
const sensitiveCall = method === 'writeFile'
return (<section className="permission">
{pluginsImages()}
<article>

Loading…
Cancel
Save