Skip to content

Commit

Permalink
app lock
Browse files Browse the repository at this point in the history
  • Loading branch information
NyaomiDEV committed Aug 13, 2024
1 parent 258e0d4 commit 0ffe8b0
Show file tree
Hide file tree
Showing 11 changed files with 202 additions and 31 deletions.
45 changes: 45 additions & 0 deletions src/lib/applock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import router from "../router";
import { securityConfig } from "./config";
import sha1 from "./util/sha1";

let isLocked = true;

export function getLockedStatus(){
if (securityConfig.password === undefined || !securityConfig.usePassword) return false;
return isLocked;
}

export function unlock(plaintextPwd: string) {
const password = new TextDecoder().decode(sha1(new TextEncoder().encode(plaintextPwd)));

if(securityConfig.password === password) {
isLocked = false;
router.replace("/");
return true;
}

return false;
}

export function disableApplock(plaintextPwd: string) {
const password = new TextDecoder().decode(sha1(new TextEncoder().encode(plaintextPwd)));

if (securityConfig.password === password) {
isLocked = false;
securityConfig.usePassword = false;
securityConfig.password = undefined;
return true;
}

return false;
}

export function enableApplock(plaintextPwd: string) {
if(isLocked) return false;

const password = new TextDecoder().decode(sha1(new TextEncoder().encode(plaintextPwd)));
securityConfig.password = password;
securityConfig.usePassword = true;

return true;
}
4 changes: 2 additions & 2 deletions src/lib/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ const defaultAccessibilityConfig: AccessibilityConfig = {
}

const defaultSecurityConfig: SecurityConfig = {
usePin: false,
pinCode: undefined,
usePassword: false,
password: undefined,
useBiometrics: false
}

Expand Down
4 changes: 2 additions & 2 deletions src/lib/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export type AccessibilityConfig = {
}

export type SecurityConfig = {
usePin: boolean,
pinCode?: string,
usePassword: boolean,
password?: string,
useBiometrics: boolean
}
2 changes: 0 additions & 2 deletions src/lib/mode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,3 @@ export function updateAccessibility() {
document.documentElement.style.removeProperty("font-size");

}

export const isAppLocked = ref(securityConfig.usePin);
37 changes: 20 additions & 17 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import i18n from "./lib/i18n";
import I18NextVue from "i18next-vue";

// Dark mode
import { isAppLocked, updateAccessibility, updateDarkMode } from "./lib/mode";
import { updateAccessibility, updateDarkMode } from "./lib/mode";

// App
import App from "./App.vue";
Expand Down Expand Up @@ -49,6 +49,7 @@ import { SplashScreen } from "@capacitor/splash-screen";
import { tryPersistStorage } from "./lib/util/storageManager";
import { getSystem, newSystem } from "./lib/db/entities/system";
import { appConfig } from "./lib/config";
import { getLockedStatus } from "./lib/applock";

if(!window.isSecureContext){
console.error("Cannot continue, this is not a safe environment!");
Expand All @@ -73,32 +74,34 @@ if(!window.isSecureContext){

const app = createApp(App).use(IonicVue).use(router).use(I18NextVue, { i18next: i18n });

router.beforeEach(() => {
// TODO: Pin lock here
return true;
})
router.beforeEach((to) => {
if(getLockedStatus()) {
if(to.path === "/lock")
return true;

router.isReady().then(async () => {
app.mount(document.body);
if (router.currentRoute.value.fullPath === "/") {
return { path: "/lock" };
}

if (to.fullPath === "/" || to.path === "/lock") {
// route to default view
switch(appConfig.view){
switch (appConfig.view) {
case "members":
await router.push("/members");
break;
return { path: "/members" };
case "journal":
await router.push("/journal");
break;
return { path: "/journal" };
case "dashboard":
default:
await router.push("/dashboard");
break;
return { path: "/dashboard" };
case "chats":
await router.push("/chats");
break;
return { path: "/chats" };
}
}

return true;
});

router.isReady().then(async () => {
app.mount(document.body);
await SplashScreen.hide();
});
}
7 changes: 6 additions & 1 deletion src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ const routes: Array<RouteRecordRaw> = [
children: [
...tabbedRoutes
]
},
},
{
path: "/lock",
name: "LockView",
component: () => import("../views/LockView.vue"),
},
...settings,
];

Expand Down
43 changes: 43 additions & 0 deletions src/views/LockView.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<script setup lang="ts">
import { IonPage, IonContent, IonLabel, IonInput } from "@ionic/vue";
import { unlock } from "../lib/applock";
function checkAndTryUnlocking(e){
console.log(unlock(e.detail.value));
}
</script>

<template>
<IonPage>
<IonContent>

<div>
<IonLabel>
<h1>{{ $t("lock:title") }}</h1>
</IonLabel>
<IonInput
fill="outline"
type="password"
labelPlacement="floating"
:label="$t('lock:hint')"
@ionInput="checkAndTryUnlocking"
></IonInput>
</div>

</IonContent>
</IonPage>
</template>

<style scoped>
div {
display: flex;
flex-direction: column;
gap: 16px;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
text-align: center;
padding: 24px;
}
</style>
74 changes: 69 additions & 5 deletions src/views/options/Security.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,72 @@
<script setup lang="ts">
import { IonContent, IonHeader, IonLabel, IonItem, IonList, IonPage, IonTitle, IonToolbar, IonToggle, IonBackButton} from '@ionic/vue';
import { inject } from 'vue';
import { IonContent, IonHeader, IonLabel, IonItem, IonList, IonPage, IonTitle, IonToolbar, IonToggle, IonBackButton, alertController } from '@ionic/vue';
import { inject, ref } from 'vue';
import { disableApplock, enableApplock } from '../../lib/applock';
import { securityConfig } from '../../lib/config';
import { useTranslation } from 'i18next-vue';
const isIOS = inject<boolean>("isIOS");
const i18next = useTranslation();
const usePassword = ref(securityConfig.usePassword);
function enterPasswordAlert(): Promise<string | null>{
return new Promise(async (resolve) => {
const alert = await alertController.create({
header: i18next.t("options:security.inputPassword.title"),
buttons: [
{
text: i18next.t("other:alerts.cancel"),
role: "cancel",
handler: () => resolve(null)
},
{
text: i18next.t("other:alerts.ok"),
role: "confirm",
handler: (e) => resolve(e.password)
}
],
inputs: [
{
name: "password",
type: "password",
placeholder: i18next.t("options:security.inputPassword.hint")
}
]
});
await alert.present();
});
}
async function modifyPassword() {
const password = await enterPasswordAlert();
if(password){
const result = enableApplock(password);
usePassword.value = result;
} else
usePassword.value = false;
}
async function disablePassword() {
const password = await enterPasswordAlert();
if(password){
const result = disableApplock(password);
usePassword.value = !result;
}
else
usePassword.value = true;
}
function toggle(){
if(usePassword.value) {
return modifyPassword();
}
return disablePassword();
}
</script>

<template>
Expand All @@ -19,17 +83,17 @@
<IonContent>
<IonList :inset="isIOS">
<IonItem>
<IonToggle>
<IonToggle @ionChange="toggle" v-model="usePassword">
<IonLabel>
<h3>{{ $t("options:security.applock.title") }}</h3>
<p>{{ $t("options:security.applock.desc") }}</p>
</IonLabel>
</IonToggle>
</IonItem>

<IonItem button :detail="true">
<IonItem button :detail="true" @click="modifyPassword" :disabled="!securityConfig.password">
<IonLabel>
<h3>{{ $t("options:security.pin") }}</h3>
<h3>{{ $t("options:security.editPassword") }}</h3>
</IonLabel>
</IonItem>

Expand Down
4 changes: 4 additions & 0 deletions translations/en/lock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"title": "The app is locked",
"hint": "Please input your app password"
}
9 changes: 7 additions & 2 deletions translations/en/options.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,14 @@
"header": "Security",
"applock": {
"title": "App lock",
"desc": "Lock the application with a 4-number PIN code"
"desc": "Lock the application with a password"
},
"pin": "Modify PIN"
"editPassword": "Modify password",
"inputPassword": {
"title": "Enter a password",
"hint": "Password"
}

},
"accessibility": {
"header": "Accessibility",
Expand Down
4 changes: 4 additions & 0 deletions translations/en/other.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,9 @@
"storagePersistence": {
"header": "Warning",
"content": "Ampersand cannot store data persistently on your device. If you're on Firefox, please give Ampersand permission to do so. If you're on a Chromium-based browser (such as Chrome, Edge, Opera, Vivaldi, etc...), please install Ampersand as a web app / add it to your Desktop/Home screen. If this message doesn't go away, consider backing up your data often from the Import/Export section."
},
"alerts": {
"ok": "Ok",
"cancel": "Cancel"
}
}

0 comments on commit 0ffe8b0

Please sign in to comment.