mirror of
https://github.com/speatzle/nfsense.git
synced 2025-05-11 19:08:20 +00:00
wip
This commit is contained in:
parent
fbc899fbe0
commit
b7daba6040
3 changed files with 95 additions and 15 deletions
|
@ -3,22 +3,62 @@ import IDashboard from '~icons/ri/dashboard-2-line';
|
||||||
import IRule from '~icons/material-symbols/rule-folder-outline-sharp';
|
import IRule from '~icons/material-symbols/rule-folder-outline-sharp';
|
||||||
import IAddress from '~icons/eos-icons/ip';
|
import IAddress from '~icons/eos-icons/ip';
|
||||||
|
|
||||||
|
import { authenticate, checkAuthentication, setup } from "./api";
|
||||||
|
|
||||||
enum NavState { Open, Reduced, Collapsed };
|
enum NavState { Open, Reduced, Collapsed };
|
||||||
const NavStateCount = 3;
|
const NavStateCount = 3;
|
||||||
let navState = $ref(NavState.Open);
|
let navState = $ref(NavState.Open);
|
||||||
let loggedOut = $ref(false);
|
|
||||||
|
|
||||||
const navRoutes = {
|
const navRoutes = {
|
||||||
"/": { icon: IDashboard, caption: "Dashboard" },
|
"/": { icon: IDashboard, caption: "Dashboard" },
|
||||||
"/rules": { icon: IRule, caption: "Rules" },
|
"/rules": { icon: IRule, caption: "Rules" },
|
||||||
"/addresses": { icon: IAddress, caption: "Addresses" },
|
"/addresses": { icon: IAddress, caption: "Addresses" },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum AuthState { Unauthenticated, MfaRequired, Authenticated };
|
||||||
|
let authState = $ref(AuthState.Unauthenticated);
|
||||||
|
let loginDisabled = $ref(true);
|
||||||
|
|
||||||
|
let username = $ref("");
|
||||||
|
let password = $ref("");
|
||||||
|
|
||||||
|
|
||||||
|
async function tryLogin() {
|
||||||
|
loginDisabled = true;
|
||||||
|
const res = await authenticate(username, password);
|
||||||
|
password = "";
|
||||||
|
loginDisabled = false;
|
||||||
|
if (res.error != null) {
|
||||||
|
console.info("authentication error");
|
||||||
|
} else {
|
||||||
|
// TODO Check for MFA here
|
||||||
|
authState = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function tryLogout() {
|
||||||
|
authState = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function deAuthenticatedCallback() {
|
||||||
|
console.info("Unauthenticated");
|
||||||
|
authState = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async() => {
|
||||||
|
setup(deAuthenticatedCallback);
|
||||||
|
let res = await checkAuthentication();
|
||||||
|
authState = res.auth;
|
||||||
|
loginDisabled = false;
|
||||||
|
if (authState > 0) {
|
||||||
|
console.info("Already Authenticated ", authState);
|
||||||
|
}
|
||||||
|
else console.info("Check Authentication error",res.error);
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="!loggedOut" :class="{
|
<div v-if="authState === AuthState.Authenticated" :class="{
|
||||||
'layout': 1,
|
'layout': 1,
|
||||||
'nav-state-open': navState === NavState.Open,
|
'nav-state-open': navState === NavState.Open,
|
||||||
'nav-state-collapsed': navState === NavState.Collapsed,
|
'nav-state-collapsed': navState === NavState.Collapsed,
|
||||||
|
@ -41,10 +81,10 @@ const navRoutes = {
|
||||||
<div class="flex-row">
|
<div class="flex-row">
|
||||||
<router-link class="button" to="/help"><i-material-symbols-help-outline/></router-link>
|
<router-link class="button" to="/help"><i-material-symbols-help-outline/></router-link>
|
||||||
<router-link class="button" to="/settings"><i-material-symbols-settings/></router-link>
|
<router-link class="button" to="/settings"><i-material-symbols-settings/></router-link>
|
||||||
<button @click="() => loggedOut = true"><i-material-symbols-logout/></button>
|
<button @click="tryLogout"><i-material-symbols-logout/></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<router-view v-slot="{ Component, route }" v-if="!loggedOut">
|
<router-view v-slot="{ Component, route }" v-if="authState === AuthState.Authenticated">
|
||||||
<Transition name="fade">
|
<Transition name="fade">
|
||||||
<component :is="Component" :key="{route}" class="page-content pad gap"/>
|
<component :is="Component" :key="{route}" class="page-content pad gap"/>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
@ -52,16 +92,16 @@ const navRoutes = {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Transition name="fade">
|
<Transition name="fade">
|
||||||
<div class="login" v-if="loggedOut">
|
<div class="login" v-if="authState === AuthState.Unauthenticated">
|
||||||
<FocusTrap>
|
<FocusTrap>
|
||||||
<form @submit="$event => $event.preventDefault()">
|
<form @submit="$event => $event.preventDefault()" :disabled="loginDisabled">
|
||||||
<h1>nfSense Login</h1>
|
<h1>nfSense Login</h1>
|
||||||
<label for="username" v-text="'Username'"/>
|
<label for="username" v-text="'Username'"/>
|
||||||
<input name="username"/>
|
<input name="username" v-model="username"/>
|
||||||
<label for="password" v-text="'Password'" type="password"/>
|
<label for="password" v-text="'Password'" type="password"/>
|
||||||
<input name="password"/>
|
<input name="password" v-model="password"/>
|
||||||
|
|
||||||
<button @click="() => loggedOut = false">Login</button>
|
<button @click="tryLogin">Login</button>
|
||||||
</form>
|
</form>
|
||||||
</FocusTrap>
|
</FocusTrap>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,11 +4,49 @@ const socktransport = new WebSocketTransport("ws://"+ window.location.host + "/w
|
||||||
const manager = new RequestManager([socktransport, httpTransport], () => crypto.randomUUID());
|
const manager = new RequestManager([socktransport, httpTransport], () => crypto.randomUUID());
|
||||||
const client = new Client(manager);
|
const client = new Client(manager);
|
||||||
|
|
||||||
export async function apiCall(method: string, params: Record<string, any>){
|
let deAuthenticatedCallback;
|
||||||
|
|
||||||
|
export function setup(_deAuthenticatedCallback: () => void) {
|
||||||
|
deAuthenticatedCallback = _deAuthenticatedCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function apiCall(method: string, params: Record<string, any>): Promise<any>{
|
||||||
try {
|
try {
|
||||||
const result = await client.request({method, params});
|
const result = await client.request({method, params});
|
||||||
console.debug("api call result", result);
|
console.debug("api call result", result);
|
||||||
} catch (ex){
|
} catch (ex){
|
||||||
console.debug("api call epic fail", ex);
|
console.debug("api call epic fail", ex);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function authenticate(username: string, password: string): Promise<any> {
|
||||||
|
const pResponse = axios.post("/login", { username, password }, {timeout: 10100});
|
||||||
|
try {
|
||||||
|
const response = await pResponse;
|
||||||
|
// Dont log this as the user password is inside: console.debug(response);
|
||||||
|
return { data: response.data, error: null};
|
||||||
|
} catch (error) {
|
||||||
|
return { data: null, error: error};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkAuthentication() {
|
||||||
|
const res = await apiCall("session-check", {});
|
||||||
|
if (res.error == "HTTP: Your Session cookie is invalid") return {auth: 0, error: null};
|
||||||
|
if (res.error == "HTTP: Your Session Requires TFA") return {auth: 1, error: null};
|
||||||
|
else if (res.error) return {auth: 0, error: res.error};
|
||||||
|
else {
|
||||||
|
/* TODO add commit_hash storing
|
||||||
|
const last_hash = window.localStorage.getItem("commit_hash");
|
||||||
|
|
||||||
|
if (last_hash) {
|
||||||
|
if (last_hash !== res.data.commit_hash) {
|
||||||
|
console.log("Detected New Backend Version, Reloading...");
|
||||||
|
window.localStorage.removeItem("commit_hash");
|
||||||
|
window.location.reload(true);
|
||||||
|
}
|
||||||
|
} else window.localStorage.setItem("commit_hash", res.data.commit_hash);
|
||||||
|
*/
|
||||||
|
return {auth: 2, error: null};
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -11,10 +11,12 @@ import (
|
||||||
type SessionKeyType string
|
type SessionKeyType string
|
||||||
|
|
||||||
const SessionKey SessionKeyType = "session"
|
const SessionKey SessionKeyType = "session"
|
||||||
|
const SessionCookieName string = "session"
|
||||||
|
|
||||||
type Session struct {
|
type Session struct {
|
||||||
Username string
|
Username string
|
||||||
Expires time.Time
|
Expires time.Time
|
||||||
|
// TODO Add []websocket.Conn pointer to close all active websockets, alternativly do this via context cancelation
|
||||||
}
|
}
|
||||||
|
|
||||||
var sessionsSync sync.Mutex
|
var sessionsSync sync.Mutex
|
||||||
|
@ -41,7 +43,7 @@ func GenerateSession(w http.ResponseWriter, username string) {
|
||||||
Username: username,
|
Username: username,
|
||||||
Expires: expires,
|
Expires: expires,
|
||||||
}
|
}
|
||||||
http.SetCookie(w, &http.Cookie{Name: "session", HttpOnly: true, SameSite: http.SameSiteStrictMode, Value: id, Expires: expires})
|
http.SetCookie(w, &http.Cookie{Name: SessionCookieName, HttpOnly: true, SameSite: http.SameSiteStrictMode, Value: id, Expires: expires})
|
||||||
}
|
}
|
||||||
|
|
||||||
func CleanupSessions(stop chan struct{}) {
|
func CleanupSessions(stop chan struct{}) {
|
||||||
|
@ -78,7 +80,7 @@ func HandleLogin(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleLogout(w http.ResponseWriter, r *http.Request) {
|
func HandleLogout(w http.ResponseWriter, r *http.Request) {
|
||||||
http.SetCookie(w, &http.Cookie{Name: "session", Value: "", Expires: time.Now()})
|
http.SetCookie(w, &http.Cookie{Name: SessionCookieName, Value: "", Expires: time.Now()})
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,6 +95,6 @@ func HandleSession(w http.ResponseWriter, r *http.Request) {
|
||||||
if s != nil {
|
if s != nil {
|
||||||
s.Expires = time.Now().Add(time.Minute * 5)
|
s.Expires = time.Now().Add(time.Minute * 5)
|
||||||
}
|
}
|
||||||
http.SetCookie(w, &http.Cookie{Name: "session", HttpOnly: true, SameSite: http.SameSiteStrictMode, Value: id, Expires: s.Expires})
|
http.SetCookie(w, &http.Cookie{Name: SessionCookieName, HttpOnly: true, SameSite: http.SameSiteStrictMode, Value: id, Expires: s.Expires})
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue