-
Notifications
You must be signed in to change notification settings - Fork 325
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add window.ipfs.enable(opts) (Bulk Permission Prompt) #619
Merged
+3,185
−1,342
Merged
Changes from 6 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
4e6f65d
feat: scaffolding for window.ipfs.enable
lidel b0110a0
feat: window.ipfs.enable()
lidel fb27d28
fix(build): regenerate yarn.lock
lidel a8db570
chore: pre-api-whitelist → pre-command
lidel 19a9577
refactor(window.ipfs): backward-compatible errors
lidel b633eb4
feat(window.ipfs): opt-in ipfsx experiment
lidel 690ad80
feat: add deprecation warning to window.ipfs.<cmd>
lidel 756b177
fix(privacy): remove ACL whitelist for window.ipfs
lidel f3e9c6c
docs(window.ipfs): updates before beta with v2
lidel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,40 @@ | ||
'use strict' | ||
|
||
const { createProxyClient } = require('ipfs-postmsg-proxy') | ||
const _Buffer = Buffer | ||
const { assign, freeze } = Object | ||
|
||
// TODO: (wip) this should not be injected by default into every page, | ||
// instead should be lazy-loaded when .enable() method is called for the first time | ||
const { createProxyClient } = require('ipfs-postmsg-proxy') | ||
|
||
function createEnableCommand (proxyClient) { | ||
return { | ||
enable: async (opts) => { | ||
// (This should be a lazy-load) | ||
// Send message to proxy server for additional validation | ||
// eg. trigger user prompt if a list of requested capabilities is not empty | ||
// or fail fast and throw if IPFS Proxy is disabled globally | ||
await require('postmsg-rpc').call('proxy.enable', opts) | ||
// Additional client-side features | ||
if (opts && opts.experiments) { | ||
if (opts.experiments.ipfsx) { | ||
// Experiment: wrap API with https://github.com/alanshaw/ipfsx | ||
return freeze(require('ipfsx')(proxyClient)) | ||
} | ||
} | ||
return freeze(proxyClient) | ||
} | ||
} | ||
} | ||
|
||
function createWindowIpfs () { | ||
const proxyClient = createProxyClient() | ||
assign(proxyClient, createEnableCommand(proxyClient)) | ||
// TODO: return thin object with lazy-init inside of window.ipfs.enable | ||
return freeze(proxyClient) | ||
} | ||
|
||
// TODO: we should remove Buffer and add support for Uint8Array/ArrayBuffer natively | ||
// See: https://github.com/ipfs/interface-ipfs-core/issues/404 | ||
window.Buffer = window.Buffer || _Buffer | ||
window.ipfs = window.ipfs || createProxyClient() | ||
window.ipfs = window.ipfs || createWindowIpfs() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
const { inCommandWhitelist, createCommandWhitelistError } = require('./pre-command') | ||
const { inNoAclPromptWhitelist, createProxyAclError } = require('./pre-acl') | ||
|
||
// Artificial API command responsible for backend orchestration | ||
// during window.ipfs.enable() | ||
function createEnableCommand (getIpfs, getState, getScope, accessControl, requestAccess) { | ||
return async (opts) => { | ||
const scope = await getScope() | ||
console.log(`[ipfs-companion] received window.ipfs.enable request from ${scope}`, opts) | ||
|
||
// Check if all access to the IPFS node is disabled | ||
if (!getState().ipfsProxy) throw new Error('User disabled access to API proxy in IPFS Companion') | ||
|
||
// NOOP if .enable() was called without any arguments | ||
if (!opts) return | ||
|
||
// Validate and prompt for any missing permissions in bulk | ||
// if a list of needed commands is announced up front | ||
if (opts.commands) { | ||
let missingAcls = [] | ||
let deniedAcls = [] | ||
for (let command of opts.commands) { | ||
// Fail fast if command is not allowed to be proxied at all | ||
if (!inCommandWhitelist(command)) { | ||
throw createCommandWhitelistError(command) | ||
} | ||
// Get the current access flag to decide if it should be added | ||
// to the list of permissions to be prompted about in the next step | ||
if (!inNoAclPromptWhitelist(command)) { | ||
let access = await accessControl.getAccess(scope, command) | ||
if (!access) { | ||
missingAcls.push(command) | ||
} else if (access.allow !== true) { | ||
deniedAcls.push(command) | ||
} | ||
} | ||
} | ||
// Fail fast if user already denied any of requested permissions | ||
if (deniedAcls.length) { | ||
throw createProxyAclError(scope, deniedAcls) | ||
} | ||
// Display a single prompt with all missing permissions | ||
if (missingAcls.length) { | ||
const { allow, wildcard } = await requestAccess(scope, missingAcls) | ||
let access = await accessControl.setAccess(scope, wildcard ? '*' : missingAcls, allow) | ||
if (!access.allow) { | ||
throw createProxyAclError(scope, missingAcls) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
module.exports = createEnableCommand |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// Some API commands are too sensitive to be exposed to dapps on every website | ||
// We follow a safe security practice of denying everything and allowing access | ||
// to a pre-approved list of known APIs. This way if a new API is added | ||
// it will be blocked by default, until it is explicitly enabled below. | ||
const COMMAND_WHITELIST = Object.freeze(require('./command-whitelist.json')) | ||
|
||
// Creates a "pre" function that is called prior to calling a real function | ||
// on the IPFS instance. It will throw if access is denied due to API not being whitelisted | ||
function createPreCommand (permission) { | ||
return async (...args) => { | ||
if (!inCommandWhitelist(permission)) { | ||
throw createCommandWhitelistError(permission) | ||
} | ||
return args | ||
} | ||
} | ||
|
||
function inCommandWhitelist (permission) { | ||
// Fail fast if API or namespace is not explicitly whitelisted | ||
const permRoot = permission.split('.')[0] | ||
return COMMAND_WHITELIST.includes(permRoot) || COMMAND_WHITELIST.includes(permission) | ||
} | ||
|
||
// Standardized error thrown when a command is not on the COMMAND_WHITELIST | ||
// TODO: return errors following conventions from https://github.com/ipfs/js-ipfs/pull/1746 | ||
function createCommandWhitelistError (permission) { | ||
const permissions = Array.isArray(permission) ? permission : [permission] | ||
console.warn(`[ipfs-companion] Access to '${permission}' commands over window.ipfs is blocked. If you feel it should be allowed, open an issue at https://github.com/ipfs-shipyard/ipfs-companion/issues/new`) | ||
const err = new Error(`Access to '${permission}' commands over IPFS Proxy is globally blocked`) | ||
err.output = { | ||
payload: { | ||
// Error follows convention from https://github.com/ipfs/js-ipfs/pull/1746/files | ||
code: 'ERR_IPFS_PROXY_ACCESS_DENIED', | ||
permissions, | ||
// TODO: remove below after deprecation period ends with Q1 | ||
get isIpfsProxyWhitelistError () { | ||
console.warn("[ipfs-companion] reading .isIpfsProxyWhitelistError from Ipfs Proxy errors is deprecated, use '.code' instead") | ||
return true | ||
}, | ||
get permission () { | ||
if (!this.permissions || !this.permissions.length) return undefined | ||
console.warn("[ipfs-companion] reading .permission from Ipfs Proxy errors is deprecated, use '.permissions' instead") | ||
return this.permissions[0] | ||
} | ||
} | ||
} | ||
return err | ||
} | ||
|
||
module.exports = { | ||
createPreCommand, | ||
createCommandWhitelistError, | ||
inCommandWhitelist, | ||
COMMAND_WHITELIST | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea is to enable opt-in experiments such as ipfsx which is used as an example: