Password Authentication
This commit is contained in:
@@ -1,17 +1,142 @@
|
||||
import React from 'react';
|
||||
import { Button, Group, Loader, LoadingOverlay, Notification, Space, TextInput, Title } from '@mantine/core';
|
||||
import { useForm } from '@mantine/hooks';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { ImCross } from 'react-icons/im';
|
||||
import { Navigate, Outlet, Route, Routes } from 'react-router-dom';
|
||||
import MainLayout from './components/MainLayout';
|
||||
import { PasswordSend, ServerStatusResponse, update_freq } from './js/models';
|
||||
import { getstatus, login, setpassword } from './js/utils';
|
||||
import HomePage from './pages/HomePage';
|
||||
import ServiceDetails from './pages/ServiceDetails';
|
||||
|
||||
function App() {
|
||||
return <Routes>
|
||||
<Route element={<MainLayout><Outlet /></MainLayout>}>
|
||||
<Route index element={<HomePage />} />
|
||||
<Route path=":srv_id" element={<ServiceDetails />} />
|
||||
<Route path="*" element={<Navigate to="/" />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [systemStatus, setSystemStatus] = useState<ServerStatusResponse>({status:"", loggined:false})
|
||||
const [reqError, setReqError] = useState<undefined|string>()
|
||||
const [error, setError] = useState<string|null>()
|
||||
const [loadinBtn, setLoadingBtn] = useState(false);
|
||||
|
||||
const getStatus = () =>{
|
||||
getstatus().then( res =>{
|
||||
setSystemStatus(res)
|
||||
setReqError(undefined)
|
||||
setLoading(false)
|
||||
}).catch(err=>{
|
||||
setReqError(err.toString())
|
||||
setLoading(false)
|
||||
setTimeout(getStatus, update_freq)
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(getStatus,[])
|
||||
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
password:"",
|
||||
},
|
||||
validationRules:{
|
||||
password: (value) => value !== ""
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
if (loading){
|
||||
return <LoadingOverlay visible/>
|
||||
}else if (reqError){
|
||||
return <div className='center-flex-row' style={{padding:"100px"}}>
|
||||
<Title order={1} align="center">Errore nel caricamento del firewall! 🔥</Title>
|
||||
<Space h="md" />
|
||||
<Title order={4} align="center">Errore nella comunicazione con il backend</Title>
|
||||
<Space h="md" />
|
||||
Errore: {reqError}
|
||||
<Space h="xl" />
|
||||
<Loader />
|
||||
</div>
|
||||
}else if (systemStatus.status === "init"){
|
||||
|
||||
const submitRequest = async (values:PasswordSend) => {
|
||||
setLoadingBtn(true)
|
||||
await setpassword(values).then(res => {
|
||||
if(!res){
|
||||
setSystemStatus({loggined:true, status:"run"})
|
||||
}else{
|
||||
setError(res)
|
||||
}
|
||||
}).catch( err => setError(err.toString()))
|
||||
setLoadingBtn(false)
|
||||
}
|
||||
|
||||
|
||||
return <div className='center-flex-row' style={{padding:"100px"}}>
|
||||
<Title order={3} align="center">Choose the password for access to the firewall 🔒</Title>
|
||||
<Space h="xl" />
|
||||
<form onSubmit={form.onSubmit(submitRequest)} style={{width:"80%"}}>
|
||||
<TextInput
|
||||
label="Password"
|
||||
placeholder="$3cr3t"
|
||||
{...form.getInputProps('password')}
|
||||
/>
|
||||
<Group position="right" mt="md">
|
||||
<Button loading={loadinBtn} type="submit">Set Password</Button>
|
||||
</Group>
|
||||
</form>
|
||||
<Space h="xl" />
|
||||
{error?<>
|
||||
<Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}>
|
||||
Error: {error}
|
||||
</Notification><Space h="md" /></>:null}
|
||||
</div>
|
||||
}else if (systemStatus.status === "run" && !systemStatus.loggined){
|
||||
const submitRequest = async (values:PasswordSend) => {
|
||||
setLoadingBtn(true)
|
||||
await login(values).then(res => {
|
||||
if(!res){
|
||||
setSystemStatus({...systemStatus, loggined:true})
|
||||
}else{
|
||||
setError(res)
|
||||
}
|
||||
}).catch( err => setError(err.toString()))
|
||||
setLoadingBtn(false)
|
||||
}
|
||||
|
||||
|
||||
return <div className='center-flex-row' style={{padding:"100px"}}>
|
||||
<Title order={2} align="center">Welcome to Firegex 🔥</Title>
|
||||
<Space h="xl" />
|
||||
<Title order={2} align="center">Before you use the firewall, insert a password 🔒</Title>
|
||||
<Space h="xl" />
|
||||
<form onSubmit={form.onSubmit(submitRequest)} style={{width:"80%"}}>
|
||||
<TextInput
|
||||
label="Password"
|
||||
placeholder="$3cr3t"
|
||||
{...form.getInputProps('password')}
|
||||
/>
|
||||
<Group position="right" mt="md">
|
||||
<Button loading={loadinBtn} type="submit">Login</Button>
|
||||
</Group>
|
||||
</form>
|
||||
<Space h="xl" />
|
||||
{error?<>
|
||||
<Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}>
|
||||
Error: {error}
|
||||
</Notification><Space h="md" /></>:null}
|
||||
</div>
|
||||
}else if (systemStatus.status === "run" && systemStatus.loggined){
|
||||
return <Routes>
|
||||
<Route element={<MainLayout><Outlet /></MainLayout>}>
|
||||
<Route index element={<HomePage />} />
|
||||
<Route path=":srv_id" element={<ServiceDetails />} />
|
||||
<Route path="*" element={<Navigate to="/" />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
}else{
|
||||
return <div className='center-flex-row' style={{padding:"100px"}}>
|
||||
<Title order={1} align="center">Errore nel caricamento del firewall! 🔥</Title>
|
||||
<Space h="md" />
|
||||
<Title order={4} align="center">Errore nella comunicazione con il backend</Title>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -73,7 +73,6 @@ function AddNewRegex({ closePopup, service }:{ closePopup:()=>void, service:stri
|
||||
|
||||
return <form onSubmit={form.onSubmit(submitRequest)}>
|
||||
<TextInput
|
||||
required
|
||||
label="Regex"
|
||||
placeholder="[A-Z0-9]{31}="
|
||||
{...form.getInputProps('regex')}
|
||||
@@ -93,14 +92,12 @@ function AddNewRegex({ closePopup, service }:{ closePopup:()=>void, service:stri
|
||||
data={['C -> S', 'S -> C', 'C <-> S']}
|
||||
label="Choose the source of the packets to filter"
|
||||
variant="filled"
|
||||
required
|
||||
{...form.getInputProps('mode')}
|
||||
/>
|
||||
<Space h="md" />
|
||||
<FilterTypeSelector
|
||||
size="md"
|
||||
color="gray"
|
||||
required
|
||||
{...form.getInputProps('type')}
|
||||
/>
|
||||
<Group position="right" mt="md">
|
||||
|
||||
@@ -41,7 +41,6 @@ function AddNewService({ closePopup }:{ closePopup:()=>void }) {
|
||||
|
||||
return <form onSubmit={form.onSubmit(submitRequest)}>
|
||||
<TextInput
|
||||
required
|
||||
label="Service name"
|
||||
placeholder="Challenge 01"
|
||||
{...form.getInputProps('name')}
|
||||
@@ -49,7 +48,6 @@ function AddNewService({ closePopup }:{ closePopup:()=>void }) {
|
||||
<Space h="md" />
|
||||
|
||||
<NumberInput
|
||||
required
|
||||
placeholder="8080"
|
||||
min={1}
|
||||
max={65535}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { ActionIcon, Badge, Image, Modal } from '@mantine/core';
|
||||
import { ActionIcon, Badge, Button, Divider, Group, Image, Menu, Modal, Notification, Space, Switch, TextInput } from '@mantine/core';
|
||||
import style from "./Header.module.scss";
|
||||
import { errorNotify, generalstats } from '../../js/utils';
|
||||
import { GeneralStats, update_freq } from '../../js/models';
|
||||
import { changepassword, errorNotify, generalstats, logout, okNotify } from '../../js/utils';
|
||||
import { ChangePassword, GeneralStats, update_freq } from '../../js/models';
|
||||
import { BsPlusLg } from "react-icons/bs"
|
||||
import { AiFillHome } from "react-icons/ai"
|
||||
import { useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import AddNewRegex from '../AddNewRegex';
|
||||
import AddNewService from '../AddNewService';
|
||||
import { MdSettings } from 'react-icons/md';
|
||||
import { FaLock } from 'react-icons/fa';
|
||||
import { ImCross, ImExit } from 'react-icons/im';
|
||||
import { useForm } from '@mantine/hooks';
|
||||
|
||||
|
||||
function Header() {
|
||||
@@ -30,9 +34,50 @@ function Header() {
|
||||
const updater = setInterval(updateInfo, update_freq)
|
||||
return () => { clearInterval(updater) }
|
||||
}, []);
|
||||
|
||||
const logout_action = () => {
|
||||
logout().then(r => {
|
||||
window.location.reload()
|
||||
}).catch(r => {
|
||||
errorNotify("Logout failed!",`Error: ${r}`)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
password:"",
|
||||
expire:true
|
||||
},
|
||||
validationRules:{
|
||||
password: (value) => value !== ""
|
||||
}
|
||||
})
|
||||
|
||||
const [loadingBtn, setLoadingBtn] = useState(false)
|
||||
const [error, setError] = useState<null|string>(null)
|
||||
const [changePasswordModal, setChangePasswordModal] = useState(false);
|
||||
|
||||
const submitRequest = async (values:ChangePassword) => {
|
||||
setLoadingBtn(true)
|
||||
await changepassword(values).then(res => {
|
||||
if(!res){
|
||||
okNotify("Password change done!","The password of the firewall has been changed!")
|
||||
setChangePasswordModal(false)
|
||||
form.reset()
|
||||
}else{
|
||||
setError(res)
|
||||
}
|
||||
}).catch( err => setError(err.toString()))
|
||||
setLoadingBtn(false)
|
||||
}
|
||||
|
||||
|
||||
|
||||
const {srv_id} = useParams()
|
||||
|
||||
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const closeModal = () => {setOpen(false);}
|
||||
|
||||
@@ -45,6 +90,13 @@ function Header() {
|
||||
<Badge style={{marginLeft:"10px"}} size="lg" color="yellow" variant="filled">Filtered Connections: {generalStats.closed}</Badge>
|
||||
<Badge style={{marginLeft:"10px"}} size="lg" color="violet" variant="filled">Regexes: {generalStats.regexes}</Badge>
|
||||
<div style={{marginLeft:"20px"}}></div>
|
||||
<Menu>
|
||||
<Menu.Label>Firewall Access</Menu.Label>
|
||||
<Menu.Item icon={<ImExit size={14} />} onClick={logout_action}>Logout</Menu.Item>
|
||||
<Divider />
|
||||
<Menu.Item color="red" icon={<FaLock size={14} />} onClick={() => setChangePasswordModal(true)}>Change Password</Menu.Item>
|
||||
</Menu>
|
||||
<div style={{marginLeft:"20px"}}></div>
|
||||
{ location.pathname !== "/"?
|
||||
<ActionIcon color="teal" style={{marginRight:"10px"}}
|
||||
size="xl" radius="md" variant="filled"
|
||||
@@ -61,8 +113,31 @@ function Header() {
|
||||
<AddNewService closePopup={closeModal} />
|
||||
</Modal>
|
||||
}
|
||||
|
||||
|
||||
<Modal size="xl" title="Change Firewall Password" opened={changePasswordModal} onClose={()=>setChangePasswordModal(false)} closeOnClickOutside={false} centered>
|
||||
|
||||
<form onSubmit={form.onSubmit(submitRequest)}>
|
||||
<Space h="md" />
|
||||
<TextInput
|
||||
label="New Password"
|
||||
placeholder="$3cr3t"
|
||||
{...form.getInputProps('password')}
|
||||
/>
|
||||
<Space h="md" />
|
||||
<Switch
|
||||
label="Expire the login status to all connections"
|
||||
{...form.getInputProps('expire', { type: 'checkbox' })}
|
||||
/>
|
||||
<Space h="md" />
|
||||
<Group position="right" mt="md">
|
||||
<Button loading={loadingBtn} type="submit">Change Password</Button>
|
||||
</Group>
|
||||
</form>
|
||||
<Space h="xl" />
|
||||
{error?<>
|
||||
<Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}>
|
||||
Error: {error}
|
||||
</Notification><Space h="md" /></>:null}
|
||||
</Modal>
|
||||
<div style={{marginLeft:"40px"}}></div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { Container, MantineProvider, Space } from '@mantine/core';
|
||||
import { NotificationsProvider } from '@mantine/notifications';
|
||||
import React from 'react';
|
||||
import { Container, Space } from '@mantine/core';
|
||||
import Footer from './Footer';
|
||||
import Header from './Header';
|
||||
|
||||
function MainLayout({ children }:{ children:any }) {
|
||||
return <>
|
||||
<MantineProvider theme={{ colorScheme: 'dark' }} withGlobalStyles withNormalizeCSS>
|
||||
<NotificationsProvider>
|
||||
|
||||
<Header />
|
||||
<Space h="xl" />
|
||||
<Container size="xl" style={{minHeight:"57.5vh"}}>
|
||||
@@ -15,8 +13,7 @@ function MainLayout({ children }:{ children:any }) {
|
||||
</Container>
|
||||
<Space h="xl" />
|
||||
<Footer />
|
||||
</NotificationsProvider>
|
||||
</MantineProvider>
|
||||
|
||||
</>
|
||||
}
|
||||
|
||||
|
||||
@@ -3,12 +3,18 @@ import ReactDOM from 'react-dom/client';
|
||||
import { BrowserRouter } from "react-router-dom"
|
||||
import './index.scss';
|
||||
import App from './App';
|
||||
import { MantineProvider } from '@mantine/core';
|
||||
import { NotificationsProvider } from '@mantine/notifications';
|
||||
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById('root') as HTMLElement
|
||||
);
|
||||
root.render(
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
<MantineProvider theme={{ colorScheme: 'dark' }} withGlobalStyles withNormalizeCSS>
|
||||
<NotificationsProvider>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</NotificationsProvider>
|
||||
</MantineProvider>
|
||||
);
|
||||
|
||||
@@ -28,6 +28,20 @@ export type ServerResponse = {
|
||||
status:string
|
||||
}
|
||||
|
||||
export type ServerStatusResponse = {
|
||||
status:string,
|
||||
loggined:boolean
|
||||
}
|
||||
|
||||
export type PasswordSend = {
|
||||
password:string
|
||||
}
|
||||
|
||||
export type ChangePassword = {
|
||||
password:string,
|
||||
expire:boolean
|
||||
}
|
||||
|
||||
export type RegexFilter = {
|
||||
id:number,
|
||||
service_id:string,
|
||||
|
||||
@@ -1,27 +1,52 @@
|
||||
import { showNotification } from "@mantine/notifications";
|
||||
import { ImCross } from "react-icons/im";
|
||||
import { TiTick } from "react-icons/ti"
|
||||
import { GeneralStats, Service, ServiceAddForm, ServerResponse, RegexFilter, notification_time, RegexAddForm } from "./models";
|
||||
import { GeneralStats, Service, ServiceAddForm, ServerResponse, RegexFilter, notification_time, RegexAddForm, ServerStatusResponse, PasswordSend, ChangePassword } from "./models";
|
||||
|
||||
var Buffer = require('buffer').Buffer
|
||||
|
||||
const custom_url = ""//"http://127.0.0.1:8080"
|
||||
const DEBUG = true
|
||||
|
||||
const custom_url = DEBUG?"http://127.0.0.1:8080":""
|
||||
|
||||
export async function getapi(path:string):Promise<any>{
|
||||
return await fetch(`${custom_url}/api/${path}`).then( res => res.json() )
|
||||
return await new Promise((resolve, reject) => {
|
||||
fetch(`${custom_url}/api/${path}`,{credentials: "same-origin"})
|
||||
.then(res => {
|
||||
if(res.status == 401) window.location.reload()
|
||||
if(!res.ok) reject(res.statusText)
|
||||
res.json().then( res => resolve(res) ).catch( err => reject(err))
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
export async function postapi(path:string,data:any):Promise<any>{
|
||||
return await fetch(`${custom_url}/api/${path}`, {
|
||||
method: 'POST',
|
||||
cache: 'no-cache',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
}).then(res => res.json());
|
||||
return await new Promise((resolve, reject) => {
|
||||
fetch(`${custom_url}/api/${path}`, {
|
||||
method: 'POST',
|
||||
credentials: "same-origin",
|
||||
cache: 'no-cache',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
}).then(res => {
|
||||
if(res.status == 401) window.location.reload()
|
||||
if(!res.ok) reject(res.statusText)
|
||||
res.json().then( res => resolve(res) ).catch( err => reject(err))
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
export async function getstatus(){
|
||||
return await getapi(`status`) as ServerStatusResponse;
|
||||
}
|
||||
|
||||
export async function generalstats(){
|
||||
return await getapi("general-stats") as GeneralStats;
|
||||
@@ -35,6 +60,26 @@ export async function serviceinfo(service_id:string){
|
||||
return await getapi(`service/${service_id}`) as Service;
|
||||
}
|
||||
|
||||
export async function logout(){
|
||||
const { status } = await getapi(`logout`) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
}
|
||||
|
||||
export async function setpassword(data:PasswordSend) {
|
||||
const { status } = await postapi("set-password",data) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
}
|
||||
|
||||
export async function changepassword(data:ChangePassword) {
|
||||
const { status } = await postapi("change-password",data) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
}
|
||||
|
||||
export async function login(data:PasswordSend) {
|
||||
const { status } = await postapi("login",data) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
}
|
||||
|
||||
export async function deleteregex(regex_id:number){
|
||||
const { status } = await getapi(`regex/${regex_id}/delete`) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
|
||||
@@ -33,7 +33,7 @@ function HomePage() {
|
||||
<LoadingOverlay visible={loader} />
|
||||
{services.length > 0?services.map( srv => <ServiceRow service={srv} key={srv.id} onClick={()=>{
|
||||
navigator("/"+srv.id)
|
||||
}} />):<><Space h="xl"/> <Title className='center-flex' order={3}>No services found! Add one clicking the button above</Title></>}
|
||||
}} />):<><Space h="xl"/> <Title className='center-flex' order={3}>No services found! Add one clicking the add button above</Title></>}
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user