Skip to content
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

Adds custom proxy access dialog #382

Merged
merged 13 commits into from
Feb 19, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 77 additions & 8 deletions add-on/src/lib/ipfs-proxy/request-access.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,86 @@
'use strict'
/* eslint-env browser */

const browser = require('webextension-polyfill')

const DIALOG_WIDTH = 540
const DIALOG_HEIGHT = 200

async function requestAccess (origin, permission) {
const msg = `Allow ${origin} to access ipfs.${permission}?`
const url = browser.extension.getURL('dist/pages/proxy-access-dialog/index.html')
const currentWin = await browser.windows.getCurrent()
const { tabs } = await browser.windows.create({
url,
width: DIALOG_WIDTH,
height: DIALOG_HEIGHT,
top: Math.round(((screen.width / 2) - (DIALOG_WIDTH / 2)) + currentWin.left),
left: Math.round(((screen.height / 2) - (DIALOG_HEIGHT / 2)) + currentWin.top),
type: 'popup'
})

// TODO: add checkbox to allow all for this origin
let allow
// Resolves with { allow, remember }
const userResponse = getUserResponse(tabs[0].id, origin, permission)
// Never resolves, might reject if user closes the tab
const userTabRemoved = getUserTabRemoved(tabs[0].id, origin, permission)

let response

try {
allow = window.confirm(msg)
} catch (err) {
console.warn('Failed to confirm, possibly not supported in this environment', err)
allow = false
// Will the user respond to or close the dialog?
response = await Promise.race([userTabRemoved, userResponse])
} finally {
userTabRemoved.destroy()
userResponse.destroy()
}

return { allow }
await browser.tabs.remove(tabs[0].id)

return response
}

function getUserResponse (tabId, origin, permission) {
let onPortConnect

const userResponse = new Promise((resolve, reject) => {
onPortConnect = port => {
if (port.name !== 'proxy-access-dialog') return

browser.runtime.onConnect.removeListener(onPortConnect)

// Tell the dialog what origin/permission it is about
port.postMessage({ origin, permission })

// Wait for the user response
const onMessage = ({ allow, remember }) => {
port.onMessage.removeListener(onMessage)
resolve({ allow, remember })
}

port.onMessage.addListener(onMessage)
}

browser.runtime.onConnect.addListener(onPortConnect)
})

userResponse.destroy = () => browser.runtime.onConnect.removeListener(onPortConnect)

return userResponse
}

// Since the dialog is a tab not a real dialog it can be closed by the user
// with no response, this function creates a promise that will reject if the tab
// is removed.
function getUserTabRemoved (tabId, origin, permission) {
let onTabRemoved

const userTabRemoved = new Promise((resolve, reject) => {
onTabRemoved = () => reject(new Error(`Failed to obtain access response for ${permission} at ${origin}`))
browser.tabs.onRemoved.addListener(onTabRemoved)
})

userTabRemoved.destroy = () => browser.tabs.onRemoved.removeListener(onTabRemoved)

return userTabRemoved
}

module.exports = requestAccess
1 change: 1 addition & 0 deletions add-on/src/lib/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ function initState (options) {
state.preloadAtPublicGateway = options.preloadAtPublicGateway
state.catchUnhandledProtocols = options.catchUnhandledProtocols
state.displayNotifications = options.displayNotifications
state.ipfsProxy = options.ipfsProxy
return state
}

Expand Down
16 changes: 16 additions & 0 deletions add-on/src/pages/proxy-access-dialog/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html class="h-100">

<head>
<title>IPFS Companion</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width" />
</head>

<body class="bg-snow-muted h-100 overflow-hidden">
<div id="root"></div>
<script src="../../ipfs-companion-common.js"></script>
<script src="proxy-access-dialog.js"></script>
</body>

</html>
12 changes: 12 additions & 0 deletions add-on/src/pages/proxy-access-dialog/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
'use strict'

const browser = require('webextension-polyfill')
const choo = require('choo')
const createProxyAccessDialogStore = require('./store')
const createProxyAccessDialogPage = require('./page')

const app = choo()

app.use(createProxyAccessDialogStore(browser.i18n, browser.runtime))
app.route('*', createProxyAccessDialogPage(browser.i18n))
app.mount('#root')
10 changes: 10 additions & 0 deletions add-on/src/pages/proxy-access-dialog/page.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@import url('node_modules/tachyons/css/tachyons.css');
@import url('node_modules/ipfs-css/ipfs.css');

.hover-bg-aqua-muted:hover {
background-color: #9ad4db;
}

.hover-bg-red-muted:hover {
background-color: #f36149;
}
60 changes: 60 additions & 0 deletions add-on/src/pages/proxy-access-dialog/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
'use strict'

const html = require('choo/html')

require('./page.css')

function createProxyAccessDialogPage (i18n) {
return function proxyAccessDialogPage (state, emit) {
const onAllow = () => emit('allow', false)
const onDeny = () => emit('deny', false)
const onRememberToggle = () => emit('rememberToggle')

const { loading, origin, permission } = state

return html`
<div class="flex flex-column pa3 h-100">
<div class="flex-auto">
${loading ? null : html`
<div>
<h1 class="sans-serif f5 lh-copy charcoal mt0">Allow ${origin} to access ipfs.${permission}?</h1>
<p class="sans-serif f6 lh-copy charcoal-muted">
<label>
<input type="checkbox" checked=${state.remember} onclick=${onRememberToggle} class="mr1" />
Apply to all permissions for ${origin}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does Apply to all IPFS API permissions for sound better or worse?
(It is more clear to me, but I am non-native speaker, so.. 🙃 )

</label>
</p>
</div>
`}
</div>
<div class="tr">
${loading ? html`
<div>
<span class="mr2">${button({ text: 'Deny', disabled: true })}</span>
${button({ text: 'Allow', disabled: true })}
</div>
` : html`
<div>
<span class="mr2">${button({ text: 'Deny', onClick: onDeny, color: 'red' })}</span>
${button({ text: 'Allow', onClick: onAllow, color: 'aqua' })}
</div>
`}
</div>
</div>
`
}
}

function button ({ onClick, color, text, disabled }) {
if (disabled) {
return html`
<button type="button" class="button-reset sans-serif bg-gray-muted f5 gray br2 bw0 ph4 pv2">${text}</button>
`
}

return html`
<button type="button" onclick=${onClick} class="button-reset sans-serif bg-${color} hover-bg-${color}-muted f5 white br2 bw0 ph4 pv2">${text}</button>
`
}

module.exports = createProxyAccessDialogPage
35 changes: 35 additions & 0 deletions add-on/src/pages/proxy-access-dialog/store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use strict'

function createProxyAccessDialogStore (i18n, runtime) {
return function proxyAccessDialogStore (state, emitter) {
state.origin = null
state.permission = null
state.loading = true
state.remember = false

const port = runtime.connect({ name: 'proxy-access-dialog' })

const onMessage = (data) => {
if (!data || !data.origin || !data.permission) return
port.onMessage.removeListener(onMessage)

state.origin = data.origin
state.permission = data.permission
state.loading = false

emitter.emit('render')
}

port.onMessage.addListener(onMessage)

emitter.on('allow', (remember) => port.postMessage({ allow: true, remember }))
emitter.on('deny', (remember) => port.postMessage({ allow: false, remember }))

emitter.on('rememberToggle', () => {
state.remember = !state.remember
emitter.emit('render')
})
}
}

module.exports = createProxyAccessDialogStore
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"build:copy": "run-s build:copy:*",
"build:copy:src": "shx mkdir -p add-on/dist && shx cp -R add-on/src/* add-on/dist",
"build:copy:wx-polyfill-lib": "shx cp node_modules/webextension-polyfill/dist/browser-polyfill.min.js add-on/dist/contentScripts/browser-polyfill.min.js",
"build:js": "browserify -p prundupify -t browserify-css -t [ browserify-package-json --global ] add-on/src/background/background.js add-on/src/options/options.js add-on/src/popup/browser-action/index.js add-on/src/popup/quick-upload.js add-on/src/pages/proxy-acl/index.js -p [ factor-bundle -o add-on/dist/background/background.js -o add-on/dist/options/options.js -o add-on/dist/popup/browser-action/browser-action.js -o add-on/dist/popup/quick-upload.js -o add-on/dist/pages/proxy-acl/proxy-acl.js ] -o add-on/dist/ipfs-companion-common.js",
"build:js": "browserify -p prundupify -t browserify-css -t [ browserify-package-json --global ] add-on/src/background/background.js add-on/src/options/options.js add-on/src/popup/browser-action/index.js add-on/src/popup/quick-upload.js add-on/src/pages/proxy-acl/index.js add-on/src/pages/proxy-access-dialog/index.js -p [ factor-bundle -o add-on/dist/background/background.js -o add-on/dist/options/options.js -o add-on/dist/popup/browser-action/browser-action.js -o add-on/dist/popup/quick-upload.js -o add-on/dist/pages/proxy-acl/proxy-acl.js -o add-on/dist/pages/proxy-access-dialog/proxy-access-dialog.js ] -o add-on/dist/ipfs-companion-common.js",
"build:content-scripts": "run-p build:content-scripts:*",
"build:content-scripts:ipfs-proxy": "run-s build:content-scripts:ipfs-proxy:*",
"build:content-scripts:ipfs-proxy:page": "browserify -p prundupify -g uglifyify -t [ browserify-package-json --global ] add-on/src/contentScripts/ipfs-proxy/page.js -o add-on/dist/contentScripts/ipfs-proxy/page.js",
Expand All @@ -24,9 +24,9 @@
"build:minimize-dist": "shx rm -rf add-on/dist/lib",
"build:bundle-extension": "web-ext build -s add-on/ -i src/ -a build/",
"watch": "npm-run-all build:copy --parallel watch:*",
"watch:js": "watchify -p prundupify -t browserify-css -t [ browserify-package-json --global ] add-on/src/background/background.js add-on/src/options/options.js add-on/src/popup/browser-action/index.js add-on/src/popup/quick-upload.js add-on/src/pages/proxy-acl/index.js -p [ factor-bundle -o add-on/dist/background/background.js -o add-on/dist/options/options.js -o add-on/dist/popup/browser-action/browser-action.js -o add-on/dist/popup/quick-upload.js -o add-on/dist/pages/proxy-acl/proxy-acl.js ] -o add-on/dist/ipfs-companion-common.js -v",
"watch:js": "watchify -p prundupify -t browserify-css -t [ browserify-package-json --global ] add-on/src/background/background.js add-on/src/options/options.js add-on/src/popup/browser-action/index.js add-on/src/popup/quick-upload.js add-on/src/pages/proxy-acl/index.js add-on/src/pages/proxy-access-dialog/index.js -p [ factor-bundle -o add-on/dist/background/background.js -o add-on/dist/options/options.js -o add-on/dist/popup/browser-action/browser-action.js -o add-on/dist/popup/quick-upload.js -o add-on/dist/pages/proxy-acl/proxy-acl.js -o add-on/dist/pages/proxy-access-dialog/proxy-access-dialog.js ] -o add-on/dist/ipfs-companion-common.js -v",
"watch:content-scripts": "run-p watch:content-scripts:*",
"watch:content-scripts:ipfs-proxy": "run-s watch:content-scripts:ipfs-proxy:*",
"watch:content-scripts:ipfs-proxy": "run-p watch:content-scripts:ipfs-proxy:*",
"watch:content-scripts:ipfs-proxy:page": "watchify -p prundupify -t [ browserify-package-json --global ] add-on/src/contentScripts/ipfs-proxy/page.js -o add-on/dist/contentScripts/ipfs-proxy/page.js -v",
"watch:content-scripts:ipfs-proxy:content": "nodemon --exec \"browserify -p prundupify -t brfs -t [ browserify-package-json --global ] -s IpfsProxyContent add-on/src/contentScripts/ipfs-proxy/content.js -o add-on/dist/contentScripts/ipfs-proxy/content.js -v\" --watch add-on/src/contentScripts/ipfs-proxy/content.js --watch add-on/dist/contentScripts/ipfs-proxy/page.js",
"test": "run-s test:*",
Expand Down Expand Up @@ -72,7 +72,7 @@
"sinon": "4.1.2",
"sinon-chrome": "2.2.1",
"standard": "10.0.3",
"uglifyify": "^4.0.5",
"uglifyify": "4.0.5",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alanshaw I think you forgot to update yarn.lock. If I run npm run yarn-build it modifies it:

--- a/yarn.lock
+++ b/yarn.lock
@@ -4374,7 +4374,7 @@ ipfs-block@^0.6.1, ipfs-block@~0.6.1:
   dependencies:
     cids "^0.5.2"
 
-ipfs-css@^0.1.0:
+ipfs-css@0.1.0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/ipfs-css/-/ipfs-css-0.1.0.tgz#0337e7e3aeddf391bbd1b7b7fb427f98b6ec5ca3"
 
@@ -9315,7 +9315,7 @@ uglify-to-browserify@~1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7"
 
-uglifyify@^4.0.5:
+uglifyify@4.0.5:
   version "4.0.5"
   resolved "https://registry.yarnpkg.com/uglifyify/-/uglifyify-4.0.5.tgz#49c1fca9828c10a5a8e8d70f191a95f7ab475911"
   dependencies:

This is why Jenkins failed:

"watchify": "3.9.0",
"web-ext": "2.3.2"
},
Expand All @@ -82,6 +82,7 @@
"file-type": "7.3.0",
"ipfs": "0.27.6",
"ipfs-api": "17.2.7",
"ipfs-css": "0.1.0",
"ipfs-postmsg-proxy": "2.3.0",
"is-ipfs": "0.3.2",
"is-svg": "2.1.0",
Expand Down
4 changes: 4 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4374,6 +4374,10 @@ ipfs-block@^0.6.1, ipfs-block@~0.6.1:
dependencies:
cids "^0.5.2"

ipfs-css@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/ipfs-css/-/ipfs-css-0.1.0.tgz#0337e7e3aeddf391bbd1b7b7fb427f98b6ec5ca3"

ipfs-multipart@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/ipfs-multipart/-/ipfs-multipart-0.1.0.tgz#5a8ed13f42e82d8bef7d2e151d8eaf5e3a30e3ea"
Expand Down