poprawki bezpieczeństwa

This commit is contained in:
2023-09-16 17:11:13 +02:00
parent 00307fadad
commit 56246f271a
17 changed files with 396 additions and 402 deletions
+17 -23
View File
@@ -10,8 +10,9 @@ import PopUpCard from './components/PopUpCard.vue';
import { RouterView } from 'vue-router';
import { AuthState } from './types/types';
import useRouteGuard from './mixins/useRouteGuard';
import { useStore } from './store';
import { baseURL, useStore } from './store';
import useLocalStorage from './mixins/useLocalStorage';
import axios from 'axios';
export default defineComponent({
components: { PopUpCard },
@@ -20,7 +21,7 @@ export default defineComponent({
const { routeAuthGuard } = useRouteGuard();
const { setupStorage } = useLocalStorage();
routeAuthGuard();
// routeAuthGuard();
setupStorage();
return {
@@ -30,28 +31,21 @@ export default defineComponent({
methods: {
async autoLogin() {
const token = window.localStorage.getItem('auth-token');
if (!token) {
this.store.authState = AuthState.UNAUTHORIZED;
return;
try {
const response = await axios.post(
'/auth/token',
{},
{
baseURL,
withCredentials: true,
}
);
this.store.user = response.data;
this.$router.push('/');
} catch (error) {
this.$router.push('/login');
}
this.store.token = token;
this.store
.fetchTokenData()
.then((res) => {
this.store.user = res.data.user;
this.store.authState = AuthState.AUTHORIZED;
this.$router.push('/');
})
.catch((err) => {
this.store.isAuthorized = false;
window.localStorage.removeItem('auth-token');
this.store.authState = AuthState.UNAUTHORIZED;
this.$router.push('/login');
});
},
},
+54 -54
View File
@@ -1,54 +1,54 @@
<template>
<div class="changes-modal">
<transition name="modal-anim">
<div class="content g-card" v-if="store.changesResponse.length > 0">
<h2>Wprowadzone zmiany</h2>
<div>
<ul class="changelog">
<li v-for="change in store.changesResponse">{{ change }}</li>
</ul>
</div>
</div>
</transition>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { useStore } from '../store';
export default defineComponent({
setup() {
return {
store: useStore(),
};
},
});
</script>
<style lang="scss" scoped>
.content {
top: 0;
transform: translateX(-50%);
max-width: 450px;
}
ul.changelog {
list-style: square;
}
// Vue transition animations
.modal-anim {
&-leave-active,
&-enter-active {
transition: all 100ms ease-in-out;
}
&-enter-from,
&-leave-to {
transform: translate(-50%, -30px);
opacity: 0.25;
}
}
</style>
<template>
<div class="changes-modal">
<transition name="modal-anim">
<div class="content g-card" v-if="store.changesResponse.length > 0">
<h2>Wprowadzone zmiany</h2>
<div>
<ul class="changelog">
<li v-for="change in store.changesResponse">{{ change }}</li>
</ul>
</div>
</div>
</transition>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { useStore } from '../store';
export default defineComponent({
setup() {
return {
store: useStore(),
};
},
});
</script>
<style lang="scss" scoped>
.content {
top: 0;
transform: translateX(-50%);
max-width: 450px;
}
ul.changelog {
list-style: square;
}
// Vue transition animations
.modal-anim {
&-leave-active,
&-enter-active {
transition: all 100ms ease-in-out;
}
&-enter-from,
&-leave-to {
transform: translate(-50%, -30px);
opacity: 0.25;
}
}
</style>
+68 -68
View File
@@ -1,68 +1,68 @@
<template>
<div class="bg-dimmer"></div>
<div class="g-card popup-card">
<div class="card_content">
<p>{{ store.alertMessage || store.confirmMessage }}</p>
</div>
<div class="card_actions">
<span v-if="store.alertMessage">
<button @click="closeCard">OK!</button>
</span>
<span v-else-if="store.confirmMessage">
<button @click="confirm">OK!</button>
<button @click="closeCard">Anuluj</button>
</span>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { useStore } from '../store';
export default defineComponent({
emits: ['confirm'],
setup() {
return {
store: useStore(),
};
},
methods: {
closeCard() {
this.store.alertMessage = '';
this.store.confirmMessage = '';
},
confirm() {
this.$emit('confirm');
this.closeCard();
},
},
});
</script>
<style lang="scss" scoped>
.bg-dimmer {
position: fixed;
z-index: 998;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: #0000004f;
}
.card_content {
text-align: center;
}
.card_actions {
display: flex;
justify-content: center;
}
</style>
<template>
<div class="bg-dimmer"></div>
<div class="g-card popup-card">
<div class="card_content">
<p>{{ store.alertMessage || store.confirmMessage }}</p>
</div>
<div class="card_actions">
<span v-if="store.alertMessage">
<button @click="closeCard">OK!</button>
</span>
<span v-else-if="store.confirmMessage">
<button @click="confirm">OK!</button>
<button @click="closeCard">Anuluj</button>
</span>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { useStore } from '../store';
export default defineComponent({
emits: ['confirm'],
setup() {
return {
store: useStore(),
};
},
methods: {
closeCard() {
this.store.alertMessage = '';
this.store.confirmMessage = '';
},
confirm() {
this.$emit('confirm');
this.closeCard();
},
},
});
</script>
<style lang="scss" scoped>
.bg-dimmer {
position: fixed;
z-index: 998;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: #0000004f;
}
.card_content {
text-align: center;
}
.card_actions {
display: flex;
justify-content: center;
}
</style>
+4 -8
View File
@@ -82,9 +82,10 @@
<script lang="ts">
import { defineComponent } from 'vue';
import routesMixin from '../mixins/routesMixin';
import { useStore } from '../store';
import { useStore, baseURL } from '../store';
import { AuthState, Availability, ChangeProp, HeaderTypes, SceneryRoutesInfo, SceneryRowItem } from '../types/types';
import { getAvailabilityValue } from '../types/typeUitls';
import axios from 'axios';
export default defineComponent({
setup() {
@@ -154,13 +155,8 @@ export default defineComponent({
if (confirmed) this.updateListToDb();
},
signOut() {
this.store.token = null;
this.store.authState = AuthState.UNAUTHORIZED;
window.localStorage.removeItem('auth-token');
window.localStorage.removeItem('user');
async signOut() {
await axios.post('/auth/logout', {}, { baseURL, withCredentials: true });
this.$router.push('/login');
},
+18 -2
View File
@@ -1,9 +1,25 @@
import { createApp } from 'vue';
import router from './router';
import App from './App.vue';
import router from './router';
import { createPinia } from 'pinia';
import { useStore } from './store';
createApp(App).use(router).use(createPinia()).mount('#app');
const pinia = createPinia();
const app = createApp(App);
app.use(pinia).use(router);
app.mount('#app');
router.beforeEach((to, from, next) => {
const store = useStore();
if (to.meta.protected && !store.user) {
next('/login');
return;
}
next();
});
+69 -69
View File
@@ -1,69 +1,69 @@
import { defineComponent } from 'vue';
import { useStore } from '../store';
import { ChangeProp, SceneryRowItem } from '../types/types';
export default defineComponent({
setup() {
return {
store: useStore(),
};
},
methods: {
addChange(sceneryData: SceneryRowItem, propName: string, oldValue: any, newValue: any) {
if (oldValue === newValue) return;
const changePropName = propName as ChangeProp;
const sceneryId = sceneryData.id;
let changeItem = this.store.changeList.find((item) => item.id == sceneryId);
if (!changeItem) {
changeItem = { id: sceneryId, name: sceneryData.name };
this.store.changeList.push(changeItem);
}
changeItem[changePropName] = newValue;
const sceneryBackup = this.store.backupList.find((scenery) => scenery.id == sceneryId);
if (!sceneryBackup) return;
if (sceneryBackup && sceneryBackup[changePropName] == changeItem[changePropName])
delete changeItem[changePropName];
if (Object.keys(changeItem).length == 2 && changeItem.id)
this.store.changeList = this.store.changeList.filter((item) => changeItem?.id != item.id);
// if (
// changeItem[changePropName] !== undefined &&
// backupChangeItem[changePropName] !== undefined &&
// changeItem[changePropName] == backupChangeItem[changePropName]
// ) {
// console.log('delete');
// delete changeItem[changePropName];
// delete backupChangeItem[changePropName];
// if (Object.keys(changeItem).length == 1 && changeItem.id)
// this.store.changeList = this.store.changeList.filter((item) => changeItem?.id != item.id);
// if (Object.keys(backupChangeItem).length == 1 && backupChangeItem.id)
// this.store.changeBackupList = this.store.changeList.filter((item) => backupChangeItem?.id != item.id);
// }
this.store.unsavedChanges = this.store.changeList.length != 0;
},
addRemovalChange(sceneryData: SceneryRowItem) {
const sceneryId = sceneryData.id;
let changeItem = this.store.changeList.find((item) => item.id == sceneryId);
if (!changeItem) this.store.changeList.push({ id: sceneryId, name: sceneryData.name, toRemove: true });
else changeItem['toRemove'] = true;
this.store.unsavedChanges = Object.keys(this.store.changeList).length != 0;
},
},
});
import { defineComponent } from 'vue';
import { useStore } from '../store';
import { ChangeProp, SceneryRowItem } from '../types/types';
export default defineComponent({
setup() {
return {
store: useStore(),
};
},
methods: {
addChange(sceneryData: SceneryRowItem, propName: string, oldValue: any, newValue: any) {
if (oldValue === newValue) return;
const changePropName = propName as ChangeProp;
const sceneryId = sceneryData.id;
let changeItem = this.store.changeList.find((item) => item.id == sceneryId);
if (!changeItem) {
changeItem = { id: sceneryId, name: sceneryData.name };
this.store.changeList.push(changeItem);
}
changeItem[changePropName] = newValue;
const sceneryBackup = this.store.backupList.find((scenery) => scenery.id == sceneryId);
if (!sceneryBackup) return;
if (sceneryBackup && sceneryBackup[changePropName] == changeItem[changePropName])
delete changeItem[changePropName];
if (Object.keys(changeItem).length == 2 && changeItem.id)
this.store.changeList = this.store.changeList.filter((item) => changeItem?.id != item.id);
// if (
// changeItem[changePropName] !== undefined &&
// backupChangeItem[changePropName] !== undefined &&
// changeItem[changePropName] == backupChangeItem[changePropName]
// ) {
// console.log('delete');
// delete changeItem[changePropName];
// delete backupChangeItem[changePropName];
// if (Object.keys(changeItem).length == 1 && changeItem.id)
// this.store.changeList = this.store.changeList.filter((item) => changeItem?.id != item.id);
// if (Object.keys(backupChangeItem).length == 1 && backupChangeItem.id)
// this.store.changeBackupList = this.store.changeList.filter((item) => backupChangeItem?.id != item.id);
// }
this.store.unsavedChanges = this.store.changeList.length != 0;
},
addRemovalChange(sceneryData: SceneryRowItem) {
const sceneryId = sceneryData.id;
let changeItem = this.store.changeList.find((item) => item.id == sceneryId);
if (!changeItem) this.store.changeList.push({ id: sceneryId, name: sceneryData.name, toRemove: true });
else changeItem['toRemove'] = true;
this.store.unsavedChanges = Object.keys(this.store.changeList).length != 0;
},
},
});
+3 -1
View File
@@ -4,6 +4,9 @@ const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'ManagerView',
meta: {
protected: true,
},
component: () => import('./views/ManagerView.vue'),
},
{
@@ -18,5 +21,4 @@ const router = createRouter({
routes,
});
export default router;
+7 -33
View File
@@ -2,7 +2,7 @@ import { defineStore } from 'pinia';
import { AuthState, ILoginResponse, IStore, IUser, SceneryRowItem } from './types/types';
import axios from 'axios';
const baseURL = import.meta.env[`VITE_API_URL${import.meta.env.DEV ? '_DEV' : ''}`];
export const baseURL = import.meta.env[`VITE_API_URL${import.meta.env.DEV ? '_DEV' : ''}`];
export const useStore = defineStore('store', {
state: () =>
@@ -20,9 +20,7 @@ export const useStore = defineStore('store', {
routesModalVisible: true,
currentStation: null,
selectedStationName: '',
token: null,
user: null,
isAuthorized: false,
notifyDiscord: true,
alertMessage: '',
@@ -37,12 +35,8 @@ export const useStore = defineStore('store', {
fetchSceneriesData() {
this.dataState = 'LOADING';
const data = axios.get<SceneryRowItem[]>(`api/getSceneries?time=${Date.now()}`, {
const data = axios.get<SceneryRowItem[]>(`api/getSceneries`, {
baseURL,
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.token}`,
},
});
data
@@ -55,13 +49,13 @@ export const useStore = defineStore('store', {
})
.catch(() => {
this.dataState = 'ERROR';
this.token = '';
this.isAuthorized = false;
});
},
updateSceneriesData(mappedChangeList: any[]) {
const response = axios.post(
async updateSceneriesData(mappedChangeList: any[]) {
console.log(mappedChangeList);
const response = await axios.post(
'/manager/updateSceneryList',
{
changeList: mappedChangeList,
@@ -69,32 +63,12 @@ export const useStore = defineStore('store', {
},
{
baseURL,
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.token}`,
},
withCredentials: true,
}
);
return response;
},
login(name: string, pwd: string) {
return axios.post<ILoginResponse>(
'auth/login',
{ username: name, password: pwd },
{
baseURL,
headers: {
'Content-Type': 'application/json',
},
}
);
},
fetchTokenData() {
return axios.post<{ user: IUser }>('auth/token', { token: this.token }, { baseURL });
},
},
getters: {
+5 -5
View File
@@ -1,5 +1,5 @@
import { Availability, AvailabilityTypes } from './types';
export function getAvailabilityValue(availability: Availability) {
return AvailabilityTypes[availability];
}
import { Availability, AvailabilityTypes } from './types';
export function getAvailabilityValue(availability: Availability) {
return AvailabilityTypes[availability];
}
+7 -8
View File
@@ -87,6 +87,11 @@ export enum AuthState {
'UNAUTHORIZED' = 2,
}
export interface IUser {
name: string;
id: number;
}
export interface IStore {
dataState: string;
authState: AuthState;
@@ -101,9 +106,8 @@ export interface IStore {
routesModalVisible: boolean;
currentStation: SceneryRowItem | null;
selectedStationName: string;
token: string | null;
user: { name: string; id: string } | null;
isAuthorized: boolean;
// token: string | null;
user: IUser | null;
notifyDiscord: boolean;
alertMessage: string;
confirmMessage: string;
@@ -117,8 +121,3 @@ export interface ILoginResponse {
token: string;
user: IUser;
}
export interface IUser {
name: string;
id: string;
}
+23 -16
View File
@@ -1,11 +1,11 @@
<template>
<div class="login" v-if="store.authState == AuthState.UNAUTHORIZED">
<div class="login">
<div class="login-header">
<img src="/icon-logo.svg" alt="logo" />
<h1>Stacjownik Station Manager</h1>
</div>
<form @submit="signIn">
<form @submit.prevent="signIn">
<label for="name">Nick</label>
<br />
<input type="text" id="name" v-model="name" />
@@ -22,8 +22,9 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { useStore } from '../store';
import { useStore, baseURL } from '../store';
import { AuthState } from '../types/types';
import axios, { HttpStatusCode } from 'axios';
enum LoginState {
INITIALIZED = 0,
@@ -52,27 +53,33 @@ export default defineComponent({
methods: {
async signIn(e: Event) {
e.preventDefault();
this.loginState = LoginState.LOADING;
// this.loginState = LoginState.LOADING;
try {
const loginData = (await this.store.login(this.name, this.password)).data;
// const loginData = (await this.store.login(this.name, this.password)).data;
const response = await axios.post(
'auth/login',
{ username: this.name, password: this.password },
{
baseURL,
withCredentials: true,
}
);
this.store.authState = AuthState.AUTHORIZED;
this.loginState = LoginState.LOADED;
// this.store.authState = AuthState.AUTHORIZED;
// this.loginState = LoginState.LOADED;
this.store.token = loginData.token;
this.store.user = loginData.user;
// this.store.token = loginData.token;
// this.store.user = loginData.user;
window.localStorage.setItem('auth-token', this.store.token);
window.localStorage.setItem('user', JSON.stringify(this.store.user));
// window.localStorage.setItem('auth-token', this.store.token);
// window.localStorage.setItem('user', JSON.stringify(this.store.user));
this.store.user = response.data;
this.$router.push('/');
this.store.fetchSceneriesData();
} catch (e: any) {
this.store.authState = AuthState.UNAUTHORIZED;
this.loginState = LoginState.LOADED;
if (!e.response || e.response.status === undefined) {
this.errorMessage = 'Wystąpił błąd podczas łączenia z serwerem!';
return false;
@@ -81,7 +88,7 @@ export default defineComponent({
const response = e.response;
const status: number = response.status;
if (status == 401) {
if (status == 400) {
this.errorMessage = 'Nieprawidłowe dane!';
return false;
}
+1 -1
View File
@@ -1,5 +1,5 @@
<template>
<div class="manager" v-if="store.authState == AuthState.AUTHORIZED">
<div class="manager">
<RoutesModal v-if="store.currentStation" />
<UpdateCard v-if="store.changesResponse.length > 0" />