Merge pull request #129 from simple-login/ac-require-tabs-on-install
Require tabs permission on install
This commit is contained in:
@@ -1,37 +1,28 @@
|
||||
import browser from "webextension-polyfill";
|
||||
import { havePermission, addPermissionListener } from "./permissions";
|
||||
import SLStorage from "../popup/SLStorage";
|
||||
|
||||
let hasAlreadySetup = false;
|
||||
|
||||
async function setupContentScript() {
|
||||
if (await havePermission("tabs")) {
|
||||
if (hasAlreadySetup) return;
|
||||
hasAlreadySetup = true;
|
||||
const apiUrl = SLStorage.get(SLStorage.SETTINGS.API_URL);
|
||||
|
||||
const apiUrl = SLStorage.get(SLStorage.SETTINGS.API_URL);
|
||||
browser.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
|
||||
if (
|
||||
tab.url.startsWith("chrome") || // chrome internal pages
|
||||
tab.url.startsWith("about") || // firefox internal pages
|
||||
tab.url.startsWith("moz") || // firefox internal pages
|
||||
tab.url.startsWith(apiUrl) // app domain
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
browser.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
|
||||
if (
|
||||
tab.url.startsWith("chrome") || // chrome internal pages
|
||||
tab.url.startsWith("about") || // firefox internal pages
|
||||
tab.url.startsWith("moz") || // firefox internal pages
|
||||
tab.url.startsWith(apiUrl) // app domain
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
browser.tabs.executeScript(tabId, {
|
||||
file: "content_script/input_tools.js",
|
||||
runAt: "document_idle",
|
||||
});
|
||||
|
||||
browser.tabs.insertCSS(tabId, {
|
||||
file: "content_script/input_tools.css",
|
||||
});
|
||||
browser.tabs.executeScript(tabId, {
|
||||
file: "content_script/input_tools.js",
|
||||
runAt: "document_idle",
|
||||
});
|
||||
}
|
||||
|
||||
browser.tabs.insertCSS(tabId, {
|
||||
file: "content_script/input_tools.css",
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
addPermissionListener(setupContentScript);
|
||||
setupContentScript();
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
handleOnClickContextMenu,
|
||||
generateAliasHandlerJS,
|
||||
} from "./context-menu";
|
||||
import { firePermissionListener } from "./permissions";
|
||||
|
||||
global.isBackgroundJS = true;
|
||||
|
||||
@@ -35,8 +34,6 @@ browser.runtime.onMessage.addListener(async function (request, sender) {
|
||||
return await handleNewRandomAlias(request.currentUrl);
|
||||
} else if (request.tag === "GET_APP_SETTINGS") {
|
||||
return await handleGetAppSettings();
|
||||
} else if (request.tag === "PERMISSIONS_CHANGED") {
|
||||
return firePermissionListener();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
import browser from "webextension-polyfill";
|
||||
|
||||
const listeners = [];
|
||||
|
||||
/**
|
||||
* To be used by background.js
|
||||
* @param {function} callback
|
||||
*/
|
||||
function addPermissionListener(callback) {
|
||||
listeners.push(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* To be used by background.js
|
||||
*/
|
||||
function firePermissionListener() {
|
||||
listeners.forEach((callback) => callback());
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used by both frontend & background
|
||||
* @param {string} name name of permission
|
||||
*/
|
||||
async function havePermission(name) {
|
||||
return await browser.permissions.contains({
|
||||
permissions: [name],
|
||||
origins: ["http://*/*", "https://*/*"],
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* To be called on frontend (on user interaction)
|
||||
* @param {string} name name of permission
|
||||
*/
|
||||
async function requestPermission(name) {
|
||||
const result = await browser.permissions.request({
|
||||
permissions: [name],
|
||||
origins: ["http://*/*", "https://*/*"],
|
||||
});
|
||||
// emit "changed" event to background
|
||||
if (result) {
|
||||
browser.runtime.sendMessage({ tag: "PERMISSIONS_CHANGED" });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export {
|
||||
havePermission,
|
||||
requestPermission,
|
||||
addPermissionListener,
|
||||
firePermissionListener,
|
||||
};
|
||||
+13
-15
@@ -16,10 +16,8 @@
|
||||
"storage",
|
||||
"cookies",
|
||||
"contextMenus",
|
||||
"https://*.simplelogin.io/*"
|
||||
],
|
||||
"optional_permissions": [
|
||||
"tabs",
|
||||
"https://*.simplelogin.io/*",
|
||||
"http://*/*",
|
||||
"https://*/*"
|
||||
],
|
||||
@@ -38,17 +36,17 @@
|
||||
]
|
||||
},
|
||||
"commands": {
|
||||
"generate-random-alias": {
|
||||
"suggested_key": {
|
||||
"default": "Ctrl+Shift+X"
|
||||
},
|
||||
"description": "Generate a random email alias"
|
||||
},
|
||||
"_execute_browser_action": {
|
||||
"suggested_key": {
|
||||
"default": "Ctrl+Shift+S"
|
||||
},
|
||||
"description":"Open the extension action menu"
|
||||
}
|
||||
"generate-random-alias": {
|
||||
"suggested_key": {
|
||||
"default": "Ctrl+Shift+X"
|
||||
},
|
||||
"description": "Generate a random email alias"
|
||||
},
|
||||
"_execute_browser_action": {
|
||||
"suggested_key": {
|
||||
"default": "Ctrl+Shift+S"
|
||||
},
|
||||
"description":"Open the extension action menu"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
-101
@@ -4,8 +4,7 @@
|
||||
<div class="col-3 screen-left">
|
||||
<div class="left-content">
|
||||
<p :class="{ active: step === 2 }" @click="toStep(2)">Account</p>
|
||||
<p :class="{ active: step === 3 }" @click="toStep(3)">Permission</p>
|
||||
<p :class="{ active: step === 4 }" @click="toStep(4)">Done</p>
|
||||
<p :class="{ active: step === 3 }" @click="toStep(3)">Done</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 screen-right">
|
||||
@@ -47,72 +46,6 @@
|
||||
</div>
|
||||
|
||||
<div class="content" v-if="step === 3">
|
||||
<div v-if="isAuthenticated">
|
||||
<h1 class="h2 text-primary">Welcome {{ userName }}!</h1>
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
<h5>Extra permission</h5>
|
||||
<p>
|
||||
SimpleLogin extension requires the
|
||||
<b v-if="isChrome"
|
||||
>Read and change all your data on the websites you visit</b
|
||||
>
|
||||
<b v-else
|
||||
>Access your data for all websites & access browser tabs</b
|
||||
>
|
||||
permission.
|
||||
</p>
|
||||
|
||||
<img
|
||||
v-if="isChrome"
|
||||
src="../images/chrome-permission-screenshot.png"
|
||||
style="width: 400px;"
|
||||
/>
|
||||
<img
|
||||
v-else
|
||||
src="../images/firefox-permission-screenshot.png"
|
||||
style="width: 400px;"
|
||||
/>
|
||||
|
||||
<p>
|
||||
Though seemingly scary, the only thing SimpleLogin does is to
|
||||
detect the email field on a page and display the icon to its
|
||||
right.
|
||||
</p>
|
||||
|
||||
<img
|
||||
alt="SimpleLogin Button demo"
|
||||
src="../images/sl-button-demo.png"
|
||||
style="width: 400px;"
|
||||
class="mb-3"
|
||||
/>
|
||||
|
||||
<p>
|
||||
SimpleLogin code is open-source on
|
||||
<a
|
||||
href="https://github.com/simple-login/browser-extension"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>GitHub</a
|
||||
>
|
||||
if you want to know about what's going behind the scenes.
|
||||
</p>
|
||||
|
||||
<br />
|
||||
<button @click="askTabsPermission()" class="btn btn-primary">
|
||||
Approve access permission
|
||||
</button>
|
||||
<button
|
||||
@click="nextStep()"
|
||||
class="btn"
|
||||
style="color: #888; font-size: 0.8em;"
|
||||
>
|
||||
Skip
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="content" v-if="step === 4">
|
||||
<h5>It's all set!</h5>
|
||||
<p>
|
||||
To start using SimpleLogin, please click on
|
||||
@@ -148,7 +81,6 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { requestPermission } from "../background/permissions";
|
||||
import SLStorage from "../popup/SLStorage";
|
||||
import axios from "axios";
|
||||
import { API_ROUTE } from "../popup/APIService";
|
||||
@@ -175,15 +107,15 @@ export default {
|
||||
this.url.createNewAccount = `${apiUrl}/auth/register?next=%2Fdashboard%2Fsetup_done`;
|
||||
this.url.login = `${apiUrl}/dashboard/setup_done`;
|
||||
|
||||
// maybe user redirected from the setup_done page
|
||||
if (this.step === 3) {
|
||||
await this.tryGetUserInfo();
|
||||
}
|
||||
//Show first step while the request is being executed
|
||||
setTimeout(async () => {
|
||||
if (await this.tryGetUserInfo()) {
|
||||
this.step = 3;
|
||||
}
|
||||
}, 1);
|
||||
|
||||
// update setup step
|
||||
const self = this;
|
||||
const updateStep = function () {
|
||||
self.step = window.location.href.match(/#step3/) ? 3 : 1;
|
||||
const updateStep = () => {
|
||||
this.step = window.location.href.match(/#step3/) ? 3 : 1;
|
||||
};
|
||||
window.addEventListener("hashchange", updateStep, false);
|
||||
updateStep();
|
||||
@@ -195,34 +127,24 @@ export default {
|
||||
nextStep() {
|
||||
this.step = this.step + 1;
|
||||
},
|
||||
async askTabsPermission() {
|
||||
if (await requestPermission("tabs")) {
|
||||
this.nextStep();
|
||||
} else {
|
||||
alert(
|
||||
"Please approve permissions. If you don't want to approve, please click Skip button."
|
||||
);
|
||||
}
|
||||
},
|
||||
async tryGetUserInfo() {
|
||||
const apiUrl = await SLStorage.get(SLStorage.SETTINGS.API_URL);
|
||||
const that = this;
|
||||
|
||||
// check api key
|
||||
axios
|
||||
.get(apiUrl + API_ROUTE.GET_USER_INFO.path, {
|
||||
headers: { Authentication: this.apiKey },
|
||||
})
|
||||
.then(async (res) => {
|
||||
that.userName = res.data.name || res.data.email;
|
||||
that.isAuthenticated = true;
|
||||
try {
|
||||
// check api key
|
||||
const response = await axios.get(
|
||||
apiUrl + API_ROUTE.GET_USER_INFO.path,
|
||||
{ headers: { Authentication: this.apiKey } }
|
||||
);
|
||||
this.userName = response.data.name || response.data.email;
|
||||
this.isAuthenticated = true;
|
||||
|
||||
await SLStorage.set(SLStorage.SETTINGS.API_KEY, this.apiKey);
|
||||
EventManager.broadcast(EventManager.EVENT.SETTINGS_CHANGED);
|
||||
})
|
||||
.catch((err) => {
|
||||
// user isn't authenticated, ignore
|
||||
});
|
||||
await SLStorage.set(SLStorage.SETTINGS.API_KEY, this.apiKey);
|
||||
EventManager.broadcast(EventManager.EVENT.SETTINGS_CHANGED);
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
<div class="p-3 container">
|
||||
<p class="font-weight-bold align-self-center">App Settings</p>
|
||||
|
||||
<table class="settings-list" v-if="isSettingsFetched">
|
||||
<tr :class="{ disabled: !hasTabsPermission }">
|
||||
<table class="settings-list">
|
||||
<tr>
|
||||
<td>
|
||||
<toggle-button
|
||||
:value="showSLButton"
|
||||
@@ -31,16 +31,6 @@
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr v-if="!hasTabsPermission">
|
||||
<td>⚠️</td>
|
||||
<td>
|
||||
SimpleLogin button requires extra permission<br />
|
||||
<span class="link cursor" @click="askTabsPermission()">
|
||||
Click here to grant access
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr v-show="showSLButton">
|
||||
<td>
|
||||
<toggle-button
|
||||
@@ -74,47 +64,31 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from "axios";
|
||||
import SLStorage from "../SLStorage";
|
||||
import EventManager from "../EventManager";
|
||||
import Navigation from "../Navigation";
|
||||
import Utils from "../Utils";
|
||||
import { callAPI, API_ROUTE, API_ON_ERR } from "../APIService";
|
||||
import {
|
||||
havePermission,
|
||||
requestPermission,
|
||||
} from "../../background/permissions";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isSettingsFetched: false,
|
||||
hasTabsPermission: false,
|
||||
showSLButton: false,
|
||||
positionSLButton: "right-inside",
|
||||
reportURISLButton: "",
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
this.fetchSettings();
|
||||
this.showSLButton = await SLStorage.get(SLStorage.SETTINGS.SHOW_SL_BUTTON);
|
||||
this.positionSLButton = await SLStorage.get(
|
||||
SLStorage.SETTINGS.SL_BUTTON_POSITION
|
||||
);
|
||||
await this.setMailToUri();
|
||||
},
|
||||
methods: {
|
||||
async fetchSettings() {
|
||||
this.hasTabsPermission = await havePermission("tabs");
|
||||
this.showSLButton = this.hasTabsPermission
|
||||
? await SLStorage.get(SLStorage.SETTINGS.SHOW_SL_BUTTON)
|
||||
: false;
|
||||
this.positionSLButton = await SLStorage.get(
|
||||
SLStorage.SETTINGS.SL_BUTTON_POSITION
|
||||
);
|
||||
await this.setMailToUri();
|
||||
this.isSettingsFetched = true;
|
||||
},
|
||||
|
||||
async handleToggleSLButton() {
|
||||
this.showSLButton = !this.showSLButton;
|
||||
await SLStorage.set(SLStorage.SETTINGS.SHOW_SL_BUTTON, this.showSLButton);
|
||||
this.fetchSettings();
|
||||
this.showSavedSettingsToast();
|
||||
},
|
||||
|
||||
@@ -135,12 +109,6 @@ export default {
|
||||
Utils.showSuccess("Settings saved");
|
||||
},
|
||||
|
||||
async askTabsPermission() {
|
||||
if (await requestPermission("tabs")) {
|
||||
this.fetchSettings();
|
||||
}
|
||||
},
|
||||
|
||||
async handleLogout() {
|
||||
await callAPI(API_ROUTE.LOGOUT, {}, {}, API_ON_ERR.IGNORE);
|
||||
await SLStorage.remove(SLStorage.SETTINGS.API_KEY);
|
||||
|
||||
@@ -15,15 +15,15 @@
|
||||
v-clipboard:error="clipboardErrorHandler"
|
||||
class="cursor"
|
||||
>
|
||||
<span class="text-success recommended-alias">{{ recommendation.alias }}</span>
|
||||
<span class="text-success recommended-alias">{{
|
||||
recommendation.alias
|
||||
}}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div>
|
||||
<form @submit.prevent="createCustomAlias">
|
||||
<div class="row mb-2">
|
||||
@@ -88,10 +88,10 @@
|
||||
<hr />
|
||||
<div class="text-center">
|
||||
<button
|
||||
:disabled="loading || !canCreate"
|
||||
style="margin-left: 15px;"
|
||||
class="btn btn-outline-primary btn-sm"
|
||||
@click="createRandomAlias"
|
||||
:disabled="loading || !canCreate"
|
||||
style="margin-left: 15px;"
|
||||
class="btn btn-outline-primary btn-sm"
|
||||
@click="createRandomAlias"
|
||||
>
|
||||
<font-awesome-icon icon="random" /> OR create a totally random alias
|
||||
</button>
|
||||
@@ -142,7 +142,6 @@
|
||||
v-clipboard="() => alias.email"
|
||||
v-clipboard:success="clipboardSuccessHandler"
|
||||
v-clipboard:error="clipboardErrorHandler"
|
||||
|
||||
>
|
||||
<a class="cursor" v-b-tooltip.hover.top="'Click to Copy'">
|
||||
{{ alias.email }}
|
||||
@@ -220,7 +219,7 @@ import EventManager from "../EventManager";
|
||||
import Navigation from "../Navigation";
|
||||
import AliasMoreOptions from "./AliasMoreOptions";
|
||||
import { callAPI, API_ROUTE, API_ON_ERR } from "../APIService";
|
||||
import tippy from 'tippy.js';
|
||||
import tippy from "tippy.js";
|
||||
|
||||
const ALIAS_PREFIX_REGEX = /^[0-9a-z-_.]+$/;
|
||||
|
||||
@@ -296,9 +295,9 @@ export default {
|
||||
|
||||
await this.loadAlias();
|
||||
|
||||
tippy('.recommended-alias', {
|
||||
content: 'Click to copy',
|
||||
placement: 'bottom',
|
||||
tippy(".recommended-alias", {
|
||||
content: "Click to copy",
|
||||
placement: "bottom",
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ import EventManager from "../EventManager";
|
||||
import Utils from "../Utils";
|
||||
import AliasMoreOptions from "./AliasMoreOptions";
|
||||
import { callAPI, API_ROUTE, API_ON_ERR } from "../APIService";
|
||||
import tippy from 'tippy.js';
|
||||
import tippy from "tippy.js";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -77,9 +77,9 @@ export default {
|
||||
// TODO showVoteScreen 1 day after user installed plugin
|
||||
else this.showVoteScreen = Utils.getRandomIntBetween(0, 10) % 2 === 0;
|
||||
|
||||
tippy('.new-alias', {
|
||||
content: 'Click to copy',
|
||||
trigger: 'manual',
|
||||
tippy(".new-alias", {
|
||||
content: "Click to copy",
|
||||
trigger: "manual",
|
||||
showOnCreate: true,
|
||||
});
|
||||
},
|
||||
|
||||
+2
-1
@@ -60,7 +60,8 @@ async function initApp() {
|
||||
process.env.NODE_ENV !== "development"
|
||||
) {
|
||||
Sentry.init({
|
||||
dsn: "https://0e2d03e61f194df9ba85a791d364088b@o336535.ingest.sentry.io/5341174",
|
||||
dsn:
|
||||
"https://0e2d03e61f194df9ba85a791d364088b@o336535.ingest.sentry.io/5341174",
|
||||
integrations: [
|
||||
new Integrations.Vue({ Vue, attachProps: true, logErrors: true }),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user