Skip to content

Commit c3909f4

Browse files
Sneezrymymindstorm
andauthored
add permissions management (#827)
* add permissions management * hide required permission by default * update permission description * update strings Co-authored-by: Brendan Early <[email protected]>
1 parent 05dbda0 commit c3909f4

File tree

12 files changed

+503
-8
lines changed

12 files changed

+503
-8
lines changed

_locales/en/messages.json

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,5 +445,59 @@
445445
},
446446
"no_entires": {
447447
"message": "No accounts to display. Add your first account now."
448+
},
449+
"permissions": {
450+
"message": "Permissions"
451+
},
452+
"permission_revoke": {
453+
"message": "Revoke"
454+
},
455+
"permission_show_required_permissions": {
456+
"message": "Show non-revocable permissions"
457+
},
458+
"permission_required": {
459+
"message": "This is a required permission and cannot be revoked."
460+
},
461+
"permission_active_tab": {
462+
"message": "Access to the current tab to scan QR codes."
463+
},
464+
"permission_storage": {
465+
"message": "Access to browser storage to store account data."
466+
},
467+
"permission_identity": {
468+
"message": "Allows sign in to 3rd party storage services."
469+
},
470+
"permission_clipboard_write": {
471+
"message": "Grants write-only access to the clipboard to copy codes to clipboard when you click on the account."
472+
},
473+
"permission_context_menus": {
474+
"message": "Adds Authenticator to context menu."
475+
},
476+
"permission_all_urls": {
477+
"message": "Access to all websites to scan QR codes."
478+
},
479+
"permission_sync_clock": {
480+
"message": "Allows clock sync with Google."
481+
},
482+
"permission_dropbox": {
483+
"message": "Allows backup to Dropbox."
484+
},
485+
"permission_dropbox_cannot_revoke": {
486+
"message": "You must disable Dropbox backup first."
487+
},
488+
"permission_drive": {
489+
"message": "Allows backup to Google Drive."
490+
},
491+
"permission_drive_cannot_revoke": {
492+
"message": "You must disable Google Drive backup first."
493+
},
494+
"permission_onedrive": {
495+
"message": "Allows backup to OneDrive."
496+
},
497+
"permission_onedrive_cannot_revoke": {
498+
"message": "You must disable OneDrive backup first."
499+
},
500+
"permission_unknown_permission": {
501+
"message": "Unknown permission. If see this message, please send a bug report."
448502
}
449503
}

sass/permissions.scss

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
@import "ui";
2+
3+
[v-cloak] {
4+
display: none;
5+
}
6+
7+
* {
8+
font-family: arial, "Microsoft YaHei";
9+
}
10+
11+
p {
12+
font-size: 16px;
13+
}
14+
15+
#permissions {
16+
width: 900px;
17+
position: relative;
18+
margin: 0 auto;
19+
}
20+
21+
h2 {
22+
margin-top: 3em;
23+
}
24+
25+
button {
26+
display: inline-grid;
27+
padding: 10px 20px;
28+
border: #ccc 1px solid;
29+
background: white;
30+
border-radius: 2px;
31+
position: relative;
32+
text-align: center;
33+
align-items: center;
34+
font-size: 16px;
35+
color: gray;
36+
cursor: pointer;
37+
outline: none;
38+
margin-left: 0px !important;
39+
40+
&:not(:disabled):hover {
41+
color: black;
42+
}
43+
}

src/components/Permissions.vue

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<template>
2+
<div id="permissions" class="theme-normal">
3+
<h1>Permissions</h1>
4+
<div>
5+
<input
6+
type="checkbox"
7+
id="showRequiredPermission"
8+
v-model="showAllPermissions"
9+
/>
10+
<label for="showRequiredPermission">{{
11+
i18n.permission_show_required_permissions
12+
}}</label>
13+
</div>
14+
<div v-for="permission in permissions" :key="permission.id">
15+
<h2>{{ permission.id }}</h2>
16+
<p>{{ permission.description }}</p>
17+
<p v-if="!permission.revocable">{{ i18n.permission_required }}</p>
18+
<button
19+
:disabled="!permission.revocable"
20+
v-if="permission.revocable"
21+
v-on:click="revoke(permission.id)"
22+
>
23+
{{ i18n.permission_revoke }}
24+
</button>
25+
</div>
26+
</div>
27+
</template>
28+
<script lang="ts">
29+
import Vue from "vue";
30+
import { Permission } from "../models/permission";
31+
32+
export default Vue.extend({
33+
computed: {
34+
permissions: function () {
35+
return this.$store.state.permissions.permissions.filter(
36+
(permission: Permission) => {
37+
return this.showAllPermissions || permission.revocable;
38+
}
39+
);
40+
},
41+
},
42+
data: function () {
43+
return {
44+
showAllPermissions: false,
45+
};
46+
},
47+
methods: {
48+
revoke(permissionId: string) {
49+
this.$store.commit("permissions/revokePermission", permissionId);
50+
},
51+
},
52+
});
53+
</script>

src/components/Popup/MenuPage.vue

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,16 @@
88
</div>
99
<div id="menuBody">
1010
<div class="menuList">
11-
<a href="licenses.html" target="_blank" style="text-decoration: none">
12-
<p v-bind:title="i18n.about">
13-
<span><IconInfo /></span>{{ i18n.about }}
11+
<p v-bind:title="i18n.advisor" v-on:click="showInfo('AdvisorPage')">
12+
<span><IconAdvisor /></span>{{ i18n.advisor }}
13+
</p>
14+
<a
15+
href="permissions.html"
16+
target="_blank"
17+
style="text-decoration: none"
18+
>
19+
<p v-bind:title="i18n.permissions">
20+
<span><IconClipboardCheck /></span>{{ i18n.permissions }}
1421
</p>
1522
</a>
1623
</div>
@@ -34,11 +41,6 @@
3441
<span><IconWrench /></span>{{ i18n.resize_popup_page }}
3542
</p>
3643
</div>
37-
<div class="menuList">
38-
<p v-bind:title="i18n.advisor" v-on:click="showInfo('AdvisorPage')">
39-
<span><IconAdvisor /></span>{{ i18n.advisor }}
40-
</p>
41-
</div>
4244
<div class="menuList">
4345
<p v-bind:title="i18n.feedback" v-on:click="openHelp()">
4446
<span><IconComments /></span>{{ i18n.feedback }}
@@ -55,6 +57,11 @@
5557
>
5658
<span><IconCode /></span>{{ i18n.source }}
5759
</p>
60+
<a href="licenses.html" target="_blank" style="text-decoration: none">
61+
<p v-bind:title="i18n.about">
62+
<span><IconInfo /></span>{{ i18n.about }}
63+
</p>
64+
</a>
5865
</div>
5966
<div id="version">Version {{ version }}</div>
6067
</div>
@@ -75,6 +82,7 @@ import IconAdvisor from "../../../svg/lightbulb.svg";
7582
import IconComments from "../../../svg/comments.svg";
7683
import IconGlobe from "../../../svg/globe.svg";
7784
import IconCode from "../../../svg/code.svg";
85+
import IconClipboardCheck from "../../../svg/clipboard-check.svg";
7886
7987
export default Vue.extend({
8088
components: {
@@ -89,6 +97,7 @@ export default Vue.extend({
8997
IconComments,
9098
IconGlobe,
9199
IconCode,
100+
IconClipboardCheck,
92101
},
93102
computed: {
94103
version: function () {

src/definitions/module-interface.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,7 @@ interface AdvisorState {
9696
insights: AdvisorInsightInterface[];
9797
ignoreList: string[];
9898
}
99+
100+
interface PermissionsState {
101+
permissions: PermissionInterface[];
102+
}

src/definitions/permission.d.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
interface ValidationResult {
2+
valid: boolean;
3+
message?: string;
4+
}
5+
6+
interface PermissionInterface {
7+
id: string;
8+
description: string;
9+
revocable: boolean;
10+
validation?: Array<() => ValidationResult>;
11+
}

src/models/permission.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export class Permission implements PermissionInterface {
2+
id: string;
3+
description: string;
4+
revocable: boolean;
5+
validation?: Array<() => ValidationResult>;
6+
7+
constructor(permission: PermissionInterface) {
8+
this.id = permission.id;
9+
this.description = permission.description;
10+
this.revocable = permission.revocable;
11+
this.validation = permission.validation;
12+
}
13+
}

src/permissions.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Vue
2+
import Vue from "vue";
3+
import Vuex from "vuex";
4+
5+
// Components
6+
import PermissionsView from "./components/Permissions.vue";
7+
import CommonComponents from "./components/common/index";
8+
9+
// Other
10+
import { loadI18nMessages } from "./store/i18n";
11+
import { Permissions } from "./store/Permissions";
12+
13+
async function init() {
14+
// i18n
15+
Vue.prototype.i18n = await loadI18nMessages();
16+
17+
// Load modules
18+
Vue.use(Vuex);
19+
20+
// Load common components globally
21+
for (const component of CommonComponents) {
22+
Vue.component(component.name, component.component);
23+
}
24+
25+
// State
26+
const store = new Vuex.Store({
27+
modules: {
28+
permissions: await new Permissions().getModule(),
29+
},
30+
});
31+
32+
const instance = new Vue({
33+
render: (h) => h(PermissionsView),
34+
store,
35+
}).$mount("#permissions");
36+
37+
// Set title
38+
try {
39+
document.title = instance.i18n.extName;
40+
} catch (e) {
41+
console.error(e);
42+
}
43+
}
44+
45+
init();

0 commit comments

Comments
 (0)