Password protect settings (#2932)

* Hide unsubscribe button on channel list if this is set in parental
controls.

* Fix github linting errors

* Removed trailing whitespace.

* Reverted yarn.lock change

* Remove package-lock.json

* Added support for password protecting settings.

* Push password handling code inside password-settings component.

* Added incorrect password message

* Replace update method with watch.

* Use updateSettingsPassword to commit value to database.

* Move password settings to the bottom (review comment)

* Moved unlock into separate component (review feedback)

* Styling password dialog

* Removed incorrect (and unused) prop value - review comment.

* Removed unused component imports

Co-authored-by: Simon Epstein <simon.epstein@67bricks.com>
This commit is contained in:
Simon Epstein 2022-12-17 14:28:17 +00:00 committed by GitHub
parent 79cd65c312
commit dbb54737c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 273 additions and 29 deletions

View File

@ -8,6 +8,11 @@ export default Vue.extend({
'ft-tooltip': FtTooltip
},
props: {
inputType: {
type: String,
required: false,
default: 'text'
},
placeholder: {
type: String,
required: true
@ -97,6 +102,11 @@ export default Vue.extend({
if (val !== oldVal) {
this.updateVisibleDataList()
}
},
value(val, oldVal) {
if (val !== oldVal) {
this.inputData = val
}
}
},
mounted: function () {
@ -264,6 +274,10 @@ export default Vue.extend({
this.inputData = text
},
focus() {
this.$refs.input.focus()
},
...mapActions([
'getYoutubeUrlInfo'
])

View File

@ -43,7 +43,7 @@
v-model="inputData"
:list="idDataList"
class="ft-input"
type="text"
:type="inputType"
:placeholder="placeholder"
:disabled="disabled"
:spellcheck="spellcheck"

View File

@ -0,0 +1,10 @@
.card {
position: relative;
width: 85%;
height: 25%;
margin: auto;
box-sizing: border-box;
}
.passwordInput {
width: 100%;
}

View File

@ -0,0 +1,58 @@
import Vue from 'vue'
import { mapActions } from 'vuex'
import FtCard from '../ft-card/ft-card.vue'
import FtInput from '../ft-input/ft-input.vue'
import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
import FtPrompt from '../ft-prompt/ft-prompt.vue'
export default Vue.extend({
name: 'PasswordDialog',
components: {
'ft-input': FtInput,
'ft-card': FtCard,
'ft-flex-box': FtFlexBox,
'ft-prompt': FtPrompt,
},
emits: ['settingsUnlocked'],
data: function() {
return {
password: '',
showIncorrectPassword: false
}
},
computed: {
getStoredPassword: function() {
return this.$store.getters.getSettingsPassword
},
incorrectPassword: function() {
return this.password !== '' && !this.unlocked
},
unlocked: function() {
return this.getStoredPassword === '' || this.password === this.getStoredPassword
}
},
watch: {
unlocked(val, oldVal) {
if (val !== oldVal) {
this.propagateUnlockStatus()
}
},
},
mounted: function () {
this.propagateUnlockStatus()
this.$refs.password.focus()
},
methods: {
handleLock: function () {
this.password = ''
this.showIncorrectPassword = false
},
propagateUnlockStatus: function() {
this.$emit('settingsUnlocked', this.unlocked)
},
...mapActions([
'updateSettingsPassword'
]),
}
})

View File

@ -0,0 +1,31 @@
<template>
<div>
<ft-card
class="card"
>
<h3>{{ $t("Settings.Password Dialog.Enter Password To Unlock") }}</h3>
<ft-flex-box>
<ft-input
ref="password"
:placeholder="$t('Settings.Password Dialog.Password')"
:show-action-button="false"
input-type="password"
class="passwordInput"
:value="password"
@input="e => password = e"
/>
</ft-flex-box>
<ft-prompt
v-if="showIncorrectPassword"
:label="$t('Settings.Password Dialog.Password Incorrect')"
:option-names="[$t('Ok')]"
:option-values="['ok']"
@click="handleLock"
/>
</ft-card>
</div>
</template>
<script src="./password-dialog.js" />
<style scoped src="./password-dialog.css" />

View File

@ -0,0 +1,58 @@
import Vue from 'vue'
import { mapActions } from 'vuex'
import FtSettingsSection from '../ft-settings-section/ft-settings-section.vue'
import FtInput from '../ft-input/ft-input.vue'
import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
import FtButton from '../ft-button/ft-button.vue'
export default Vue.extend({
name: 'PasswordSettings',
components: {
'ft-settings-section': FtSettingsSection,
'ft-input': FtInput,
'ft-flex-box': FtFlexBox,
'ft-button': FtButton,
},
emits: ['settingsUnlocked'],
data: function() {
return {
password: '',
}
},
computed: {
getStoredPassword: function() {
return this.$store.getters.getSettingsPassword
},
hasStoredPassword: function() {
return this.getStoredPassword !== ''
},
unlocked: function() {
return this.getStoredPassword === '' || this.password === this.getStoredPassword
}
},
watch: {
unlocked(val, oldVal) {
if (val !== oldVal) {
this.propagateUnlockStatus()
}
},
},
methods: {
handleSetPassword: function () {
this.updateSettingsPassword(this.password)
this.password = ''
},
handleRemovePassword: function () {
this.updateSettingsPassword('')
this.password = ''
},
propagateUnlockStatus: function() {
this.$emit('settingsUnlocked', this.unlocked)
},
...mapActions([
'updateSettingsPassword'
]),
}
})

View File

@ -0,0 +1,36 @@
<template>
<ft-settings-section
:title="$t('Settings.Password Settings.Password Settings')"
>
<div>
<ft-flex-box
v-if="hasStoredPassword"
class="settingsFlexStart460px"
>
<ft-button
:label="$t('Settings.Password Settings.Remove Password')"
@click="handleRemovePassword"
/>
</ft-flex-box>
<ft-flex-box
v-else
class="settingsFlexStart460px"
>
<ft-input
:placeholder="$t('Settings.Password Settings.Set Password To Prevent Access')"
:show-action-button="false"
:show-label="true"
input-type="password"
:value="password"
@input="e => password = e"
/>
<ft-button
:label="$t('Settings.Password Settings.Set Password')"
@click="handleSetPassword"
/>
</ft-flex-box>
</div>
</ft-settings-section>
</template>
<script src="./password-settings.js" />

View File

@ -274,7 +274,8 @@ const state = {
screenshotAskPath: false,
screenshotFolderPath: '',
screenshotFilenamePattern: '%Y%M%D-%H%N%S',
fetchSubscriptionsAutomatically: true
fetchSubscriptionsAutomatically: true,
settingsPassword: ''
}
const stateWithSideEffects = {

View File

@ -12,6 +12,8 @@ import ProxySettings from '../../components/proxy-settings/proxy-settings.vue'
import SponsorBlockSettings from '../../components/sponsor-block-settings/sponsor-block-settings.vue'
import ParentControlSettings from '../../components/parental-control-settings/parental-control-settings.vue'
import ExperimentalSettings from '../../components/experimental-settings/experimental-settings.vue'
import PasswordSettings from '../../components/password-settings/password-settings.vue'
import PasswordDialog from '../../components/password-dialog/password-dialog.vue'
export default Vue.extend({
name: 'Settings',
@ -28,11 +30,23 @@ export default Vue.extend({
'sponsor-block-settings': SponsorBlockSettings,
'download-settings': DownloadSettings,
'parental-control-settings': ParentControlSettings,
'experimental-settings': ExperimentalSettings
'experimental-settings': ExperimentalSettings,
'password-settings': PasswordSettings,
'password-dialog': PasswordDialog,
},
data: function () {
return {
settingsUnlocked: false
}
},
computed: {
usingElectron: function () {
return process.env.IS_ELECTRON
}
},
},
methods: {
handleSettingsUnlocked: function (n) {
this.settingsUnlocked = n
},
}
})

View File

@ -1,30 +1,41 @@
<template>
<div>
<general-settings />
<hr>
<theme-settings />
<hr>
<player-settings />
<hr>
<external-player-settings v-if="usingElectron" />
<hr v-if="usingElectron">
<subscription-settings />
<hr>
<distraction-settings />
<hr>
<privacy-settings />
<hr>
<data-settings />
<hr>
<proxy-settings />
<hr>
<download-settings v-if="usingElectron" />
<hr v-if="usingElectron">
<parental-control-settings />
<hr>
<sponsor-block-settings />
<hr v-if="usingElectron">
<experimental-settings v-if="usingElectron" />
<div v-if="settingsUnlocked">
<general-settings />
<hr>
<theme-settings />
<hr>
<player-settings />
<hr>
<external-player-settings v-if="usingElectron" />
<hr v-if="usingElectron">
<subscription-settings />
<hr>
<distraction-settings />
<hr>
<privacy-settings />
<hr>
<data-settings />
<hr>
<proxy-settings />
<hr>
<download-settings v-if="usingElectron" />
<hr v-if="usingElectron">
<parental-control-settings />
<hr>
<sponsor-block-settings />
<hr v-if="usingElectron">
<experimental-settings v-if="usingElectron" />
<hr>
<password-settings
@settingsUnlocked="handleSettingsUnlocked"
/>
</div>
<div v-else>
<password-dialog
@settingsUnlocked="handleSettingsUnlocked"
/>
</div>
</div>
</template>

View File

@ -414,6 +414,16 @@ Settings:
Experimental Settings: Experimental Settings
Warning: These settings are experimental, they make cause crashes while enabled. Making backups is highly recommended. Use at your own risk!
Replace HTTP Cache: Replace HTTP Cache
Password Dialog:
Password: Password
Enter Password To Unlock: Enter password to unlock settings
Password Incorrect: Password Incorrect
Unlock: Unlock
Password Settings:
Password Settings: Password Settings
Set Password To Prevent Access: Set a password to prevent access to settings
Set Password: Set Password
Remove Password: Remove Password
About:
#On About page
About: About
@ -825,3 +835,4 @@ Screenshot Error: Screenshot failed. {error}
Yes: Yes
No: No
Ok: Ok