mirror of
https://github.com/speatzle/nfsense.git
synced 2025-05-10 18:38:22 +00:00
basic auth / session stuff working
This commit is contained in:
parent
b7daba6040
commit
b80bc4d32f
6 changed files with 144 additions and 23 deletions
|
@ -15,6 +15,7 @@
|
|||
"@open-rpc/client-js": "^1.8.1",
|
||||
"@vueuse/core": "^9.13.0",
|
||||
"@vueuse/head": "^1.1.15",
|
||||
"axios": "^1.3.4",
|
||||
"events": "^3.3.0",
|
||||
"focus-trap": "^7.3.1",
|
||||
"focus-trap-vue": "^4.0.2",
|
||||
|
|
63
client/pnpm-lock.yaml
generated
63
client/pnpm-lock.yaml
generated
|
@ -12,6 +12,7 @@ specifiers:
|
|||
'@vue-macros/volar': ^0.8.4
|
||||
'@vueuse/core': ^9.13.0
|
||||
'@vueuse/head': ^1.1.15
|
||||
axios: ^1.3.4
|
||||
eslint: ^8.35.0
|
||||
eslint-plugin-vue: ^9.9.0
|
||||
events: ^3.3.0
|
||||
|
@ -37,6 +38,7 @@ dependencies:
|
|||
'@open-rpc/client-js': 1.8.1
|
||||
'@vueuse/core': 9.13.0_vue@3.2.47
|
||||
'@vueuse/head': 1.1.15_vue@3.2.47
|
||||
axios: 1.3.4
|
||||
events: 3.3.0
|
||||
focus-trap: 7.3.1
|
||||
focus-trap-vue: 4.0.2_oggptlzwchqpaguemspe4ract4
|
||||
|
@ -1507,11 +1509,25 @@ packages:
|
|||
'@babel/types': 7.21.2
|
||||
dev: true
|
||||
|
||||
/asynckit/0.4.0:
|
||||
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
||||
dev: false
|
||||
|
||||
/available-typed-arrays/1.0.5:
|
||||
resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dev: true
|
||||
|
||||
/axios/1.3.4:
|
||||
resolution: {integrity: sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==}
|
||||
dependencies:
|
||||
follow-redirects: 1.15.2
|
||||
form-data: 4.0.0
|
||||
proxy-from-env: 1.1.0
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
dev: false
|
||||
|
||||
/balanced-match/1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
dev: true
|
||||
|
@ -1646,6 +1662,13 @@ packages:
|
|||
resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==}
|
||||
dev: true
|
||||
|
||||
/combined-stream/1.0.8:
|
||||
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
dependencies:
|
||||
delayed-stream: 1.0.0
|
||||
dev: false
|
||||
|
||||
/concat-map/0.0.1:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
dev: true
|
||||
|
@ -1741,6 +1764,11 @@ packages:
|
|||
resolution: {integrity: sha512-+uO4+qr7msjNNWKYPHqN/3+Dx3NFkmIzayk2L1MyZQlvgZb/J1A0fo410dpKrN2SnqFjt8n4JL8fDJE0wIgjFQ==}
|
||||
dev: true
|
||||
|
||||
/delayed-stream/1.0.0:
|
||||
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
dev: false
|
||||
|
||||
/destr/1.2.2:
|
||||
resolution: {integrity: sha512-lrbCJwD9saUQrqUfXvl6qoM+QN3W7tLV5pAOs+OqOmopCCz/JkE05MHedJR1xfk4IAnZuJXPVuN5+7jNA2ZCiA==}
|
||||
dev: true
|
||||
|
@ -2140,12 +2168,31 @@ packages:
|
|||
tabbable: 6.1.1
|
||||
dev: false
|
||||
|
||||
/follow-redirects/1.15.2:
|
||||
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
|
||||
engines: {node: '>=4.0'}
|
||||
peerDependencies:
|
||||
debug: '*'
|
||||
peerDependenciesMeta:
|
||||
debug:
|
||||
optional: true
|
||||
dev: false
|
||||
|
||||
/for-each/0.3.3:
|
||||
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
|
||||
dependencies:
|
||||
is-callable: 1.2.7
|
||||
dev: true
|
||||
|
||||
/form-data/4.0.0:
|
||||
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
|
||||
engines: {node: '>= 6'}
|
||||
dependencies:
|
||||
asynckit: 0.4.0
|
||||
combined-stream: 1.0.8
|
||||
mime-types: 2.1.35
|
||||
dev: false
|
||||
|
||||
/fs-minipass/2.1.0:
|
||||
resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
|
||||
engines: {node: '>= 8'}
|
||||
|
@ -2800,6 +2847,18 @@ packages:
|
|||
braces: 3.0.2
|
||||
picomatch: 2.3.1
|
||||
|
||||
/mime-db/1.52.0:
|
||||
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
dev: false
|
||||
|
||||
/mime-types/2.1.35:
|
||||
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
dependencies:
|
||||
mime-db: 1.52.0
|
||||
dev: false
|
||||
|
||||
/mimic-fn/2.1.0:
|
||||
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
|
||||
engines: {node: '>=6'}
|
||||
|
@ -3086,6 +3145,10 @@ packages:
|
|||
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
|
||||
dev: true
|
||||
|
||||
/proxy-from-env/1.1.0:
|
||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||
dev: false
|
||||
|
||||
/prr/1.0.1:
|
||||
resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==}
|
||||
dev: true
|
||||
|
|
|
@ -3,7 +3,7 @@ import IDashboard from '~icons/ri/dashboard-2-line';
|
|||
import IRule from '~icons/material-symbols/rule-folder-outline-sharp';
|
||||
import IAddress from '~icons/eos-icons/ip';
|
||||
|
||||
import { authenticate, checkAuthentication, setup } from "./api";
|
||||
import { authenticate, logout, checkAuthentication, setup } from "./api";
|
||||
|
||||
enum NavState { Open, Reduced, Collapsed };
|
||||
const NavStateCount = 3;
|
||||
|
@ -31,17 +31,18 @@ async function tryLogin() {
|
|||
console.info("authentication error");
|
||||
} else {
|
||||
// TODO Check for MFA here
|
||||
authState = 1;
|
||||
authState = AuthState.Authenticated;
|
||||
}
|
||||
}
|
||||
|
||||
async function tryLogout() {
|
||||
authState = 0;
|
||||
logout();
|
||||
authState = AuthState.Unauthenticated;
|
||||
}
|
||||
|
||||
function deAuthenticatedCallback() {
|
||||
console.info("Unauthenticated");
|
||||
authState = 0;
|
||||
authState = AuthState.Unauthenticated;
|
||||
}
|
||||
|
||||
onMounted(async() => {
|
||||
|
@ -49,7 +50,7 @@ onMounted(async() => {
|
|||
let res = await checkAuthentication();
|
||||
authState = res.auth;
|
||||
loginDisabled = false;
|
||||
if (authState > 0) {
|
||||
if (authState === AuthState.Authenticated) {
|
||||
console.info("Already Authenticated ", authState);
|
||||
}
|
||||
else console.info("Check Authentication error",res.error);
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { RequestManager, HTTPTransport, WebSocketTransport, Client } from "@open-rpc/client-js";
|
||||
import axios from "axios";
|
||||
const httpTransport = new HTTPTransport("http://"+ window.location.host +"/api");
|
||||
const socktransport = new WebSocketTransport("ws://"+ window.location.host + "/ws/api");
|
||||
const manager = new RequestManager([socktransport, httpTransport], () => crypto.randomUUID());
|
||||
// const socktransport = new WebSocketTransport("ws://"+ window.location.host + "/ws/api");
|
||||
const manager = new RequestManager([httpTransport], () => crypto.randomUUID());
|
||||
const client = new Client(manager);
|
||||
|
||||
let deAuthenticatedCallback;
|
||||
|
@ -30,23 +31,31 @@ export async function authenticate(username: string, password: string): Promise<
|
|||
}
|
||||
}
|
||||
|
||||
export async function logout(): Promise<any> {
|
||||
const pResponse = axios.post("/logout", null, {timeout: 10100});
|
||||
try {
|
||||
const response = await pResponse;
|
||||
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 pResponse = axios.post("/session", null, {timeout: 10100});
|
||||
try {
|
||||
const response = await pResponse;
|
||||
const last_hash = window.localStorage.getItem("commit_hash");
|
||||
|
||||
if (last_hash) {
|
||||
if (last_hash !== res.data.commit_hash) {
|
||||
if (last_hash !== response.data.commit_hash) {
|
||||
console.log("Detected New Backend Version, Reloading...");
|
||||
window.localStorage.removeItem("commit_hash");
|
||||
window.location.reload(true);
|
||||
window.location.reload();
|
||||
}
|
||||
} else window.localStorage.setItem("commit_hash", res.data.commit_hash);
|
||||
*/
|
||||
} else window.localStorage.setItem("commit_hash", response.data.commit_hash);
|
||||
return {auth: 2, error: null};
|
||||
} catch (error) {
|
||||
return {auth: 0, error: error};
|
||||
}
|
||||
}
|
|
@ -18,6 +18,9 @@ export default defineConfig({
|
|||
server: {
|
||||
"proxy": {
|
||||
"/api": "http://localhost:8080",
|
||||
"/login": "http://localhost:8080",
|
||||
"/logout": "http://localhost:8080",
|
||||
"/session": "http://localhost:8080",
|
||||
"/ws": {
|
||||
target: "ws://localhost:8080",
|
||||
ws: true,
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
type SessionKeyType string
|
||||
|
@ -19,9 +23,29 @@ type Session struct {
|
|||
// TODO Add []websocket.Conn pointer to close all active websockets, alternativly do this via context cancelation
|
||||
}
|
||||
|
||||
type LoginRequest struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type SessionResponse struct {
|
||||
CommitHash string `json:"commit_hash"`
|
||||
}
|
||||
|
||||
var sessionsSync sync.Mutex
|
||||
var sessions map[string]*Session = map[string]*Session{}
|
||||
|
||||
var CommitHash = func() string {
|
||||
if info, ok := debug.ReadBuildInfo(); ok {
|
||||
for _, setting := range info.Settings {
|
||||
if setting.Key == "vcs.revision" {
|
||||
return setting.Value
|
||||
}
|
||||
}
|
||||
}
|
||||
return "asd"
|
||||
}()
|
||||
|
||||
func GetSession(r *http.Request) (string, *Session) {
|
||||
c, err := r.Cookie("session")
|
||||
if err != nil {
|
||||
|
@ -69,18 +93,28 @@ func CleanupSessions(stop chan struct{}) {
|
|||
}
|
||||
|
||||
func HandleLogin(w http.ResponseWriter, r *http.Request) {
|
||||
username := r.PostFormValue("username")
|
||||
password := r.PostFormValue("password")
|
||||
if username == "admin" && password == "12345" {
|
||||
GenerateSession(w, username)
|
||||
buf, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
slog.Error("Reading Body", err)
|
||||
return
|
||||
}
|
||||
var req LoginRequest
|
||||
err = json.Unmarshal(buf, &req)
|
||||
if err != nil {
|
||||
slog.Error("Unmarshal", err)
|
||||
return
|
||||
}
|
||||
if req.Username == "admin" && req.Password == "12345" {
|
||||
slog.Info("User Login Successfull")
|
||||
GenerateSession(w, req.Username)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
http.Redirect(w, r, "/", http.StatusFound)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
func HandleLogout(w http.ResponseWriter, r *http.Request) {
|
||||
http.SetCookie(w, &http.Cookie{Name: SessionCookieName, Value: "", Expires: time.Now()})
|
||||
http.SetCookie(w, &http.Cookie{Name: SessionCookieName, HttpOnly: true, SameSite: http.SameSiteStrictMode, Value: "", Expires: time.Now()})
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
|
@ -97,4 +131,14 @@ func HandleSession(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
http.SetCookie(w, &http.Cookie{Name: SessionCookieName, HttpOnly: true, SameSite: http.SameSiteStrictMode, Value: id, Expires: s.Expires})
|
||||
w.WriteHeader(http.StatusOK)
|
||||
resp := SessionResponse{
|
||||
CommitHash: CommitHash,
|
||||
}
|
||||
res, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(res)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue