UNPKG

remix-ide

Version:
203 lines (187 loc) 7 kB
/* global localStorage */ const yo = require('yo-yo') const csjs = require('csjs-inject') const addTooltip = require('./tooltip') const modalDialog = require('./modaldialog') const globalRegistry = require('../../global/registry') const css = csjs` .permission h4 { text-transform: uppercase; text-align: center; } .permission h6 { text-transform: uppercase; } .remember { display: flex; justify-content: space-between; align-items: center; } .images { display: flex; justify-content: center; align-items: center; padding: 10px; } .images img { width: 40px; height: 40px; } .images i { margin: 0 20px; } ` function notAllowWarning (from, to, method) { return `${from.displayName || from.name} is not allowed to call ${method} method of ${to.displayName || to.name}.` } export class PermissionHandler { constructor () { this.permissions = this._getFromLocal() this.currentVersion = 1 // here we remove the old permissions saved before adding 'permissionVersion' // since with v1 the structure has been changed because of new engine ^0.2.0-alpha.6 changes if (!localStorage.getItem('permissionVersion')) { localStorage.setItem('plugins/permissions', '') localStorage.setItem('permissionVersion', this.currentVersion) } } _getFromLocal () { const permission = localStorage.getItem('plugins/permissions') return permission ? JSON.parse(permission) : {} } persistPermissions () { const permissions = JSON.stringify(this.permissions) localStorage.setItem('plugins/permissions', permissions) } clear () { localStorage.removeItem('plugins/permissions') addTooltip('All Permissions have been reset') } /** * Show a message to ask the user for a permission * @param {PluginProfile} from The name and hash of the plugin that make the call * @param {ModuleProfile} to The name of the plugin that receive the call * @param {string} method The name of the function to be called * @param {string} message from the caller plugin to add more details if needed * @returns {Promise<{ allow: boolean; remember: boolean }} Answer from the user to the permission */ async openPermission (from, to, method, message) { return new Promise((resolve, reject) => { modalDialog( `Permission needed for ${to.displayName || to.name}`, this.form(from, to, method, message), { label: 'Accept', fn: () => { if (this.permissions[to.name][method][from.name]) { this.permissions[to.name][method][from.name] = { allow: true, hash: from.hash } this.persistPermissions() } resolve(true) } }, { label: 'Decline', fn: () => { if (this.permissions[to.name][method][from.name]) { this.permissions[to.name][method][from.name] = { allow: false, hash: from.hash } this.persistPermissions() } reject(notAllowWarning(from, to, method)) } } ) }) } /** * Check if a plugin has the permission to call another plugin and askPermission if needed * @param {PluginProfile} from the profile of the plugin that make the call * @param {ModuleProfile} to The profile of the module that receive the call * @param {string} method The name of the function to be called * @param {string} message from the caller plugin to add more details if needed * @returns {Promise<boolean>} */ async askPermission (from, to, method, message) { 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) const { allow, hash } = this.permissions[to.name][method][from.name] if (!allow) { const warning = notAllowWarning(from, to, method) addTooltip(warning) return false } return hash === from.hash ? true // Allow : this.openPermission(from, to, method, message) // New version of a plugin } catch (err) { throw new Error(err) } } /** * The permission form * @param {PluginProfile} from The name and hash of the plugin that make the call * @param {ModuleProfile} to The name of the plugin that receive the call * @param {string} method The name of te methode to be called * @param {string} message from the caller plugin to add more details if needed */ form (from, to, method, message) { const fromName = from.displayName || from.name const toName = to.displayName || to.name const remember = this.permissions[to.name][method][from.name] const switchMode = (e) => { e.target.checked ? this.permissions[to.name][method][from.name] = {} : delete this.permissions[to.name][method][from.name] } const rememberSwitch = remember ? yo`<input type="checkbox" onchange="${switchMode}" checkbox class="form-check-input" id="remember" data-id="permissionHandlerRememberChecked">` : yo`<input type="checkbox" onchange="${switchMode}" class="form-check-input" id="remember" data-id="permissionHandlerRememberUnchecked">` const text = `"${fromName}" ${(remember ? `has changed and` : ``)} would like to access to "${method}" of "${toName}"` const imgFrom = yo`<img id="permissionModalImagesFrom" src="${from.icon}" />` const imgTo = yo`<img id="permissionModalImagesTo" src="${to.icon}" />` const pluginsImages = yo` <article class="${css.images}"> ${imgFrom} <i class="fas fa-arrow-right"></i> ${imgTo} </article> ` globalRegistry.get('themeModule').api.fixInvert(imgFrom) globalRegistry.get('themeModule').api.fixInvert(imgTo) const pluginMessage = message ? yo` <div> <h6>Description</h6> <p>${message}</p> </div> ` : `` return yo` <section class="${css.permission}"> ${pluginsImages} <article> <h4 data-id="permissionHandlerMessage">${text} :</h4> <h6>${fromName}</h6> <p>${from.description || yo`<i>No description Provided</i>`}</p> <h6>${toName} :</p> <p>${to.description || yo`<i>No description Provided</i>`}</p> ${pluginMessage} </article> <article class="${css.remember}"> <div class="form-check"> ${rememberSwitch} <label class="form-check-label" for="remember" data-id="permissionHandlerRememberChoice">Remember this choice</label> </div> <button class="btn btn-sm" onclick="${_ => this.clear()}">Reset all Permissions</button> </article> </section> ` } }