diff --git a/html/src/app.js b/html/src/app.js
index d8cfeaaa..923abff8 100644
--- a/html/src/app.js
+++ b/html/src/app.js
@@ -21,6 +21,7 @@ import sharedRepository from './repository/shared.js';
import configRepository from './repository/config.js';
import webApiService from './service/webapi.js';
import gameLogService from './service/gamelog.js';
+import security from './security.js';
speechSynthesis.getVoices();
@@ -743,7 +744,7 @@ speechSynthesis.getVoices();
if (API.currentUser.status === 'busy') {
this.$message({
message: 'You can\'t invite yourself in \'Do Not Disturb\' mode',
- type: 'error'
+ type: 'error'
});
return;
}
@@ -3728,16 +3729,20 @@ speechSynthesis.getVoices();
this.updateGameLogLoop();
this.$nextTick(function () {
this.$el.style.display = '';
- this.loginForm.loading = true;
- API.getConfig().catch((err) => {
- this.loginForm.loading = false;
- throw err;
- }).then((args) => {
- API.getCurrentUser().finally(() => {
+ if (!this.enablePrimaryPassword) {
+ this.loginForm.loading = true;
+ API.getConfig().catch((err) => {
this.loginForm.loading = false;
+ throw err;
+ }).then((args) => {
+ API.getCurrentUser().finally(() => {
+ this.loginForm.loading = false;
+ });
+ return args;
});
- return args;
- });
+ } else {
+ this.loginForm.loading = false;
+ }
});
}
};
@@ -4962,6 +4967,81 @@ speechSynthesis.getVoices();
$app.updateStoredUser(this.currentUser);
});
+ $app.methods.checkPrimaryPassword = function (args) {
+ return new Promise((resolve, reject) => {
+ if (!this.enablePrimaryPassword) {
+ return resolve(args.password);
+ }
+ $app.$prompt(
+ 'Please enter your Primary Password.',
+ 'Primary Password Required',
+ {
+ inputType: "password",
+ inputPattern: /[\s\S]{1,32}/
+ },
+ ).then(({value}) => {
+ security.decrypt(args.password, value).then(pwd => {
+ return resolve(pwd);
+ }).catch(_ => {
+ return reject();
+ })
+ }).catch(_=>{
+ return reject();
+ })
+ })
+ }
+
+ $app.data.enablePrimaryPassword = !!configRepository.getBool('enablePrimaryPassword');
+ $app.data.enablePrimaryPasswordDialog = {
+ visible: false,
+ password: '',
+ rePassword: '',
+ beforeClose: function (done) {
+ $app._data.enablePrimaryPassword = false;
+ done();
+ }
+ };
+ $app.methods.enablePrimaryPasswordChange = function () {
+ this.enablePrimaryPasswordDialog.password = '';
+ this.enablePrimaryPasswordDialog.rePassword = '';
+ if (this.enablePrimaryPassword) {
+ this.enablePrimaryPasswordDialog.visible = true;
+ } else {
+ $app.$prompt(
+ 'Please enter your Primary Password.',
+ 'Primary Password Required',
+ {
+ inputType: "password",
+ inputPattern: /[\s\S]{1,32}/
+ },
+ ).then(({value}) => {
+ for (let name in this.loginForm.savedCredentials) {
+ security.decrypt(this.loginForm.savedCredentials[name].loginParmas.password, value).then(plaintext => {
+ this.loginForm.savedCredentials[name].loginParmas.password = plaintext;
+ }).catch(_ => {
+ this.enablePrimaryPassword = true;
+ })
+ }
+ this.savePrimaryPassword();
+ }).catch(_ => {
+ this.enablePrimaryPassword = true;
+ this.savePrimaryPassword();
+ })
+ }
+ }
+ $app.methods.savePrimaryPassword = function () {
+ configRepository.setBool('enablePrimaryPassword', this.enablePrimaryPassword);
+ this.enablePrimaryPasswordDialog.visible = false;
+ if(this.enablePrimaryPassword) {
+ let key = this.enablePrimaryPasswordDialog.password;
+ for (let name in this.loginForm.savedCredentials) {
+ security.encrypt(this.loginForm.savedCredentials[name].loginParmas.password, key).then(plaintext => {
+ this.loginForm.savedCredentials[name].loginParmas.password = plaintext;
+ })
+ }
+ }
+ }
+
$app.methods.updateStoredUser = function (currentUser) {
var savedCredentialsArray = {};
if (configRepository.getString('savedCredentials') !== null) {
@@ -4982,21 +5062,28 @@ speechSynthesis.getVoices();
};
$app.methods.relogin = function (loginParmas) {
- this.loginForm.loading = true;
- return API.getConfig().catch((err) => {
- this.loginForm.loading = false;
- throw err;
- }).then(() => {
- API.login({
- username: loginParmas.username,
- password: loginParmas.password
- }).catch((err2) => {
- API.logout();
- throw err2;
- }).finally(() => {
- this.loginForm.loading = false;
- });
- });
+ return new Promise((resolve, reject) => {
+ this.checkPrimaryPassword(loginParmas).then(pwd => {
+ this.loginForm.loading = true;
+ return API.getConfig().catch((err) => {
+ this.loginForm.loading = false;
+ return reject(err);
+ }).then(() => {
+ API.login({
+ username: loginParmas.username,
+ password: pwd
+ }).catch((err2) => {
+ this.loginForm.loading = false;
+ API.logout();
+ return reject(err2);
+ }).then(() => {
+ return resolve();
+ });
+ });
+ }).catch(_ => {
+ return reject();
+ })
+ })
};
$app.methods.deleteSavedLogin = function (username) {
@@ -9839,7 +9926,7 @@ speechSynthesis.getVoices();
if (API.currentUser.status === 'busy') {
this.$message({
message: 'You can\'t invite yourself in \'Do Not Disturb\' mode',
- type: 'error'
+ type: 'error'
});
return;
}
diff --git a/html/src/index.pug b/html/src/index.pug
index 0ff8b167..aa60b2fe 100644
--- a/html/src/index.pug
+++ b/html/src/index.pug
@@ -738,6 +738,9 @@ html
div.options-container-item
span.name VRCPlus Profile Icons
el-switch(v-model="displayVRCPlusIconsAsAvatar")
+ div.options-container-item
+ span.name Use a Primary Password
+ el-switch(v-model="enablePrimaryPassword" @change="enablePrimaryPasswordChange")
div.options-container
span.header Side Pannel Sorting Options
div.options-container-item
@@ -2377,4 +2380,37 @@ html
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+
+ //- dialog: Enable primary password
+ el-dialog.x-dialog(
+ :visible.sync="enablePrimaryPasswordDialog.visible"
+ :before-close="enablePrimaryPasswordDialog.beforeClose"
+ ref="primaryPasswordDialog"
+ :close-on-click-modal="false"
+ title="Primary Password Required"
+ width="400px"
+ )
+ el-input(
+ v-model="enablePrimaryPasswordDialog.password"
+ placeholder="Input new password"
+ type="password"
+ size="mini"
+ maxlength="32"
+ show-password
+ autofocus
+ )
+ el-input(
+ v-model="enablePrimaryPasswordDialog.rePassword"
+ placeholder="Re-input password"
+ type="password"
+ style="margin-top:5px"
+ size="mini"
+ maxlength="32"
+ show-password
+ )
+ template(#footer)
+ el-button(
+ type="primary" size="small" @click="savePrimaryPassword"
+ :disabled="enablePrimaryPasswordDialog.password.length===0||enablePrimaryPasswordDialog.password!==enablePrimaryPasswordDialog.rePassword"
+ ) OK
script(src="app.js")