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
+1 -1
View File
@@ -1,3 +1,3 @@
VITE_API_URL="https://stacjownik.spythere.pl" VITE_API_URL="https://stacjownik.spythere.pl"
VITE_API_URL_DEV="http://localhost:3001" VITE_API_URL_DEV="http://localhost:3001"
VITE_API_DEV=0 VITE_API_DEV=1
+17 -23
View File
@@ -10,8 +10,9 @@ import PopUpCard from './components/PopUpCard.vue';
import { RouterView } from 'vue-router'; import { RouterView } from 'vue-router';
import { AuthState } from './types/types'; import { AuthState } from './types/types';
import useRouteGuard from './mixins/useRouteGuard'; import useRouteGuard from './mixins/useRouteGuard';
import { useStore } from './store'; import { baseURL, useStore } from './store';
import useLocalStorage from './mixins/useLocalStorage'; import useLocalStorage from './mixins/useLocalStorage';
import axios from 'axios';
export default defineComponent({ export default defineComponent({
components: { PopUpCard }, components: { PopUpCard },
@@ -20,7 +21,7 @@ export default defineComponent({
const { routeAuthGuard } = useRouteGuard(); const { routeAuthGuard } = useRouteGuard();
const { setupStorage } = useLocalStorage(); const { setupStorage } = useLocalStorage();
routeAuthGuard(); // routeAuthGuard();
setupStorage(); setupStorage();
return { return {
@@ -30,28 +31,21 @@ export default defineComponent({
methods: { methods: {
async autoLogin() { async autoLogin() {
const token = window.localStorage.getItem('auth-token'); try {
if (!token) { const response = await axios.post(
this.store.authState = AuthState.UNAUTHORIZED; '/auth/token',
return; {},
{
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');
});
}, },
}, },
+4 -8
View File
@@ -82,9 +82,10 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import routesMixin from '../mixins/routesMixin'; 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 { AuthState, Availability, ChangeProp, HeaderTypes, SceneryRoutesInfo, SceneryRowItem } from '../types/types';
import { getAvailabilityValue } from '../types/typeUitls'; import { getAvailabilityValue } from '../types/typeUitls';
import axios from 'axios';
export default defineComponent({ export default defineComponent({
setup() { setup() {
@@ -154,13 +155,8 @@ export default defineComponent({
if (confirmed) this.updateListToDb(); if (confirmed) this.updateListToDb();
}, },
signOut() { async signOut() {
this.store.token = null; await axios.post('/auth/logout', {}, { baseURL, withCredentials: true });
this.store.authState = AuthState.UNAUTHORIZED;
window.localStorage.removeItem('auth-token');
window.localStorage.removeItem('user');
this.$router.push('/login'); this.$router.push('/login');
}, },
+18 -2
View File
@@ -1,9 +1,25 @@
import { createApp } from 'vue'; import { createApp } from 'vue';
import router from './router';
import App from './App.vue'; import App from './App.vue';
import router from './router';
import { createPinia } from 'pinia'; 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();
});
+3 -1
View File
@@ -4,6 +4,9 @@ const routes: Array<RouteRecordRaw> = [
{ {
path: '/', path: '/',
name: 'ManagerView', name: 'ManagerView',
meta: {
protected: true,
},
component: () => import('./views/ManagerView.vue'), component: () => import('./views/ManagerView.vue'),
}, },
{ {
@@ -18,5 +21,4 @@ const router = createRouter({
routes, routes,
}); });
export default router; 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 { AuthState, ILoginResponse, IStore, IUser, SceneryRowItem } from './types/types';
import axios from 'axios'; 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', { export const useStore = defineStore('store', {
state: () => state: () =>
@@ -20,9 +20,7 @@ export const useStore = defineStore('store', {
routesModalVisible: true, routesModalVisible: true,
currentStation: null, currentStation: null,
selectedStationName: '', selectedStationName: '',
token: null,
user: null, user: null,
isAuthorized: false,
notifyDiscord: true, notifyDiscord: true,
alertMessage: '', alertMessage: '',
@@ -37,12 +35,8 @@ export const useStore = defineStore('store', {
fetchSceneriesData() { fetchSceneriesData() {
this.dataState = 'LOADING'; this.dataState = 'LOADING';
const data = axios.get<SceneryRowItem[]>(`api/getSceneries?time=${Date.now()}`, { const data = axios.get<SceneryRowItem[]>(`api/getSceneries`, {
baseURL, baseURL,
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.token}`,
},
}); });
data data
@@ -55,13 +49,13 @@ export const useStore = defineStore('store', {
}) })
.catch(() => { .catch(() => {
this.dataState = 'ERROR'; this.dataState = 'ERROR';
this.token = '';
this.isAuthorized = false;
}); });
}, },
updateSceneriesData(mappedChangeList: any[]) { async updateSceneriesData(mappedChangeList: any[]) {
const response = axios.post( console.log(mappedChangeList);
const response = await axios.post(
'/manager/updateSceneryList', '/manager/updateSceneryList',
{ {
changeList: mappedChangeList, changeList: mappedChangeList,
@@ -69,32 +63,12 @@ export const useStore = defineStore('store', {
}, },
{ {
baseURL, baseURL,
headers: { withCredentials: true,
'Content-Type': 'application/json',
Authorization: `Bearer ${this.token}`,
},
} }
); );
return response; 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: { getters: {
+7 -8
View File
@@ -87,6 +87,11 @@ export enum AuthState {
'UNAUTHORIZED' = 2, 'UNAUTHORIZED' = 2,
} }
export interface IUser {
name: string;
id: number;
}
export interface IStore { export interface IStore {
dataState: string; dataState: string;
authState: AuthState; authState: AuthState;
@@ -101,9 +106,8 @@ export interface IStore {
routesModalVisible: boolean; routesModalVisible: boolean;
currentStation: SceneryRowItem | null; currentStation: SceneryRowItem | null;
selectedStationName: string; selectedStationName: string;
token: string | null; // token: string | null;
user: { name: string; id: string } | null; user: IUser | null;
isAuthorized: boolean;
notifyDiscord: boolean; notifyDiscord: boolean;
alertMessage: string; alertMessage: string;
confirmMessage: string; confirmMessage: string;
@@ -117,8 +121,3 @@ export interface ILoginResponse {
token: string; token: string;
user: IUser; user: IUser;
} }
export interface IUser {
name: string;
id: string;
}
+23 -16
View File
@@ -1,11 +1,11 @@
<template> <template>
<div class="login" v-if="store.authState == AuthState.UNAUTHORIZED"> <div class="login">
<div class="login-header"> <div class="login-header">
<img src="/icon-logo.svg" alt="logo" /> <img src="/icon-logo.svg" alt="logo" />
<h1>Stacjownik Station Manager</h1> <h1>Stacjownik Station Manager</h1>
</div> </div>
<form @submit="signIn"> <form @submit.prevent="signIn">
<label for="name">Nick</label> <label for="name">Nick</label>
<br /> <br />
<input type="text" id="name" v-model="name" /> <input type="text" id="name" v-model="name" />
@@ -22,8 +22,9 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { useStore } from '../store'; import { useStore, baseURL } from '../store';
import { AuthState } from '../types/types'; import { AuthState } from '../types/types';
import axios, { HttpStatusCode } from 'axios';
enum LoginState { enum LoginState {
INITIALIZED = 0, INITIALIZED = 0,
@@ -52,27 +53,33 @@ export default defineComponent({
methods: { methods: {
async signIn(e: Event) { async signIn(e: Event) {
e.preventDefault(); // this.loginState = LoginState.LOADING;
this.loginState = LoginState.LOADING;
try { 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.store.authState = AuthState.AUTHORIZED;
this.loginState = LoginState.LOADED; // this.loginState = LoginState.LOADED;
this.store.token = loginData.token; // this.store.token = loginData.token;
this.store.user = loginData.user; // this.store.user = loginData.user;
window.localStorage.setItem('auth-token', this.store.token); // window.localStorage.setItem('auth-token', this.store.token);
window.localStorage.setItem('user', JSON.stringify(this.store.user)); // window.localStorage.setItem('user', JSON.stringify(this.store.user));
this.store.user = response.data;
this.$router.push('/'); this.$router.push('/');
this.store.fetchSceneriesData(); this.store.fetchSceneriesData();
} catch (e: any) { } catch (e: any) {
this.store.authState = AuthState.UNAUTHORIZED;
this.loginState = LoginState.LOADED;
if (!e.response || e.response.status === undefined) { if (!e.response || e.response.status === undefined) {
this.errorMessage = 'Wystąpił błąd podczas łączenia z serwerem!'; this.errorMessage = 'Wystąpił błąd podczas łączenia z serwerem!';
return false; return false;
@@ -81,7 +88,7 @@ export default defineComponent({
const response = e.response; const response = e.response;
const status: number = response.status; const status: number = response.status;
if (status == 401) { if (status == 400) {
this.errorMessage = 'Nieprawidłowe dane!'; this.errorMessage = 'Nieprawidłowe dane!';
return false; return false;
} }
+1 -1
View File
@@ -1,5 +1,5 @@
<template> <template>
<div class="manager" v-if="store.authState == AuthState.AUTHORIZED"> <div class="manager">
<RoutesModal v-if="store.currentStation" /> <RoutesModal v-if="store.currentStation" />
<UpdateCard v-if="store.changesResponse.length > 0" /> <UpdateCard v-if="store.changesResponse.length > 0" />
+10 -4
View File
@@ -1,7 +1,13 @@
import { defineConfig } from 'vite' import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue';
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [vue()] plugins: [vue()],
}) build: {
commonjsOptions: {
esmExternals: true,
},
},
});