Commit 0c8be407 authored by Rémi PAILHAREY's avatar Rémi PAILHAREY
Browse files

Authentification completed + enhance navbar

parent 6b027467
......@@ -2,7 +2,6 @@ package auth
import (
"context"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
......@@ -74,7 +73,6 @@ func ValidateAuthMiddleware(next http.Handler, allowedRoles []string, checkXSRF
user := TokenData{}
checkXSRF, err := tokens.Manager.ExtractAndValidateToken(r, authTokenKey, &user, checkXSRF)
if err != nil {
log.Logger.Println("ERREUR EXTRACTION")
// Handle CORS preflight requests
if r.Method == "OPTIONS" {
return
......@@ -102,9 +100,6 @@ func ValidateAuthMiddleware(next http.Handler, allowedRoles []string, checkXSRF
http.Error(w, "XSRF protection triggered", http.StatusUnauthorized)
return
}
if checkXSRF && r.Header.Get("XSRF-TOKEN") == user.XSRFToken {
log.Logger.Println("token xsrf validé")
}
err = checkUserHasRole(user, allowedRoles)
if err != nil {
http.Error(w, err.Error(), http.StatusForbidden)
......@@ -213,7 +208,7 @@ func GetShareToken(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, token)
}
// GetTokenData gets an user from a request
// GetTokenData gets a user from a request
func GetTokenData(r *http.Request) (TokenData, error) {
user, ok := r.Context().Value(ContextData).(TokenData)
if !ok {
......@@ -221,21 +216,3 @@ func GetTokenData(r *http.Request) (TokenData, error) {
}
return user, nil
}
// getUserDirectly directly checks if an user is allowed to connect
func getUserDirectly(authorizationHeader string) (TokenData, error) {
authHeader := strings.Split(authorizationHeader, " ")
var user User
if authHeader[0] == "Basic" && len(authHeader) == 2 {
decoded, err := base64.StdEncoding.DecodeString(authHeader[1])
if err == nil {
auth := strings.Split(string(decoded), ":")
sentUser := User{Login: auth[0], Password: auth[1]}
foundUser, err := MatchUser(sentUser)
if err == nil {
return (TokenData{User: foundUser}), nil
}
}
}
return TokenData{User: user}, errors.New("could not retrieve user directly from basic auth header")
}
......@@ -41,7 +41,7 @@ func main() {
// mainMux.Handle("/api/common/", http.StripPrefix("/api/common", auth.ValidateAuthMiddleware(commonMux, []string{"*"}, true)))
adminMux := http.NewServeMux()
adminMux.HandleFunc("/api/admin/newToken", createNewToken)
adminMux.HandleFunc("/newToken", createNewToken)
mainMux.Handle("/api/admin/", http.StripPrefix("/api/admin", auth.ValidateAuthMiddleware(adminMux, []string{os.Getenv("ADMIN_ROLE")}, true)))
http.ListenAndServe(":8080", mainMux)
......
This diff is collapsed.
This diff is collapsed.
// Imports
import * as Navbar from "/components/navbar/navbar.js";
// import { loginModes } from "/assets/brand/brand.js";
import * as Auth from "/services/auth/auth.js";
import { HandleError } from "/services/common/errors.js";
import { IsEmpty } from "/services/common/common.js";
// DOM elements
let mountpoint;
let login_field;
let password_field;
let login_inmemory;
let login_icon;
export class Login {
constructor(user, navbar) {
this.user = user;
this.navbar = navbar;
}
export async function mount(where) {
mountpoint = where;
let user = await Auth.GetUser();
if (user !== undefined) {
document.getElementById(mountpoint).innerHTML = "";
location.hash = "#";
} else {
Navbar.CreateMenu();
document.getElementById(mountpoint).innerHTML = /* HTML */ `
<div class="columns">
<div class="column is-half is-offset-one-quarter">
<div class="card">
<div class="card-content">
<div class="field">
<p class="control has-icons-left has-icons-right">
<input
id="login-login"
class="input"
type="text"
placeholder="Login"
/>
<span class="icon is-small is-left">
<i class="fas fa-user"></i>
</span>
</p>
</div>
<div class="field">
<p class="control has-icons-left">
<input
id="login-password"
class="input"
type="password"
placeholder="Password"
/>
<span class="icon is-small is-left">
<i class="fas fa-lock"></i>
</span>
</p>
// DOM elements
login_field;
password_field;
login_inmemory;
login_icon;
async mount(mountpoint) {
if (!IsEmpty(this.user)) {
document.getElementById(mountpoint).innerHTML = "";
location.hash = "#";
} else {
this.navbar.CreateMenu();
document.getElementById(mountpoint).innerHTML = /* HTML */ `
<div class="columns">
<div class="column is-half is-offset-one-quarter">
<div class="card">
<div class="card-content">
<div class="field">
<p class="control has-icons-left has-icons-right">
<input
id="login-login"
class="input"
type="text"
placeholder="Login"
/>
<span class="icon is-small is-left">
<i class="fas fa-user"></i>
</span>
</p>
</div>
<div class="field">
<p class="control has-icons-left">
<input
id="login-password"
class="input"
type="password"
placeholder="Password"
/>
<span class="icon is-small is-left">
<i class="fas fa-lock"></i>
</span>
</p>
</div>
</div>
<footer class="card-footer">
<a id="login-inmemory" class="card-footer-item">
<span class="icon" id="login-icon"
><i class="fas fa-key"></i></span
>Login
</a>
</footer>
</div>
<footer class="card-footer">
<a id="login-inmemory" class="card-footer-item">
<span class="icon" id="login-icon"
><i class="fas fa-key"></i></span
>Login
</a>
</footer>
</div>
</div>
</div>
`;
registerModalFields();
}
}
function registerModalFields() {
login_field = document.getElementById("login-login");
password_field = document.getElementById("login-password");
password_field.addEventListener("keyup", function (event) {
// Number 13 is the "Enter" key on the keyboard
if (event.keyCode === 13) {
doLogin();
`;
this.registerModalFields();
}
});
login_inmemory = document.getElementById("login-inmemory");
login_inmemory.addEventListener("click", function () {
doLogin();
});
login_icon = document.getElementById("login-icon");
}
}
async function doLogin() {
login_icon.classList.add("fa-pulse");
try {
const response = await fetch("/Login", {
method: "POST",
body: JSON.stringify({
login: login_field.value,
password: password_field.value,
}),
registerModalFields() {
this.login_field = document.getElementById("login-login");
this.password_field = document.getElementById("login-password");
this.password_field.addEventListener("keyup", (event) => {
// Number 13 is the "Enter" key on the keyboard
if (event.keyCode === 13) {
this.doLogin();
}
});
this.login_inmemory = document.getElementById("login-inmemory");
this.login_inmemory.addEventListener("click", function () {
this.doLogin();
});
if (response.status !== 200) {
throw new Error(`Login error (status ${response.status})`);
this.login_icon = document.getElementById("login-icon");
}
async doLogin() {
this.login_icon.classList.add("fa-pulse");
try {
const response = await fetch("/Login", {
method: "POST",
body: JSON.stringify({
login: this.login_field.value,
password: this.password_field.value,
}),
});
if (response.status !== 200) {
throw new Error(`Login error (status ${response.status})`);
}
const newUser = await Auth.GetUser();
Object.assign(this.user, newUser);
location.hash = "#accueil";
this.navbar.CreateMenu();
} catch (e) {
HandleError(e);
this.login_icon.classList.remove("fa-pulse");
}
await Auth.GetUser();
location.hash = "#accueil";
Navbar.CreateMenu();
} catch (e) {
HandleError(e);
login_icon.classList.remove("fa-pulse");
}
}
// Imports
import * as Auth from "/services/auth/auth.js";
//import * as brand from "/assets/brand/brand.js";
import { AnimateCSS } from "/services/common/common.js";
import { AnimateCSS, IsEmpty } from "/services/common/common.js";
// local variables
let user;
let menu;
export class Navbar {
constructor(user) {
this.user = user;
}
// local variables
user;
menu;
export function mount(mountpoint) {
const where = document.getElementById(mountpoint);
where.innerHTML = /* HTML */ `
<div class="navbar-brand">
<a
role="button"
id="navbar-burger"
class="navbar-burger burger"
aria-label="menu"
aria-expanded="false"
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div id="navbar-menu" class="navbar-menu"></div>
`;
// Hamburger menu
const burger = document.getElementById("navbar-burger");
menu = document.getElementById("navbar-menu");
const openClose = async (e) => {
if (burger.classList.contains("is-active")) {
await AnimateCSS(menu, "slideOutRight");
menu.classList.remove("is-active");
burger.classList.remove("is-active");
} else {
if (e.srcElement == burger) {
menu.classList.add("is-active");
burger.classList.add("is-active");
AnimateCSS(menu, "slideInRight");
mount(mountpoint) {
const where = document.getElementById(mountpoint);
// window.document.title = brand.windowTitle;
where.innerHTML = /* HTML */ `
<div class="navbar-brand">
<a class="navbar-item is-size-4" href="/">
<img src="assets/icons/icon.png" alt="logo" /> Cyber-Signal
</a>
<a role="button" id="navbar-burger" class="navbar-burger burger" aria-label="menu" aria-expanded="false">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div id="navbar-menu" class="navbar-menu"></div>
`;
// Hamburger menu
const burger = document.getElementById("navbar-burger");
this.menu = document.getElementById("navbar-menu");
const openClose = async (e) => {
if (burger.classList.contains("is-active")) {
await AnimateCSS(this.menu, "slideOutRight");
this.menu.classList.remove("is-active");
burger.classList.remove("is-active");
} else {
if (e.srcElement == burger || e.srcElement.offsetParent == burger) {
this.menu.classList.add("is-active");
burger.classList.add("is-active");
AnimateCSS(this.menu, "slideInRight");
}
}
}
};
burger.addEventListener("click", openClose);
menu.addEventListener("click", openClose);
CreateMenu();
}
};
burger.addEventListener("click", openClose);
this.menu.addEventListener("click", openClose);
this.CreateMenu();
}
export async function CreateMenu() {
user = await Auth.GetUser();
console.log(user);
menu.innerHTML = /* HTML */ `
<div class="navbar-start">
<a id="navbar-accueil" class="navbar-item" href="#accueil">
<i class="navbar-menu-icon fas fa-home"></i>Accueil
</a>
${user === undefined
? ``
: /* HTML */ `
<a id="navbar-dashboard" class="navbar-item" href="#dashboard">
<i class="navbar-menu-icon fas fa-folder-open"></i>Dashboard
</a>
${user.isAdmin
? /* HTML */ `
<a
id="navbar-sharetoken"
class="navbar-item"
href="#sharetoken"
>
<i class="navbar-menu-icon fas fa-folder-open"></i>Share
token
</a>
`
: ""}
`}
</div>
<div class="navbar-end">
${user === undefined
? /* HTML */ `
<a class="navbar-item" href="#login">
<i class="navbar-menu-icon fas fa-sign-in-alt"></i>Log in
</a>
`
: /* HTML */ `
<a class="navbar-item" href="/Logout">
<i class="navbar-menu-icon fas fa-sign-out-alt"></i>Log out
</a>
`}
</div>
`;
SetActiveItem();
}
async CreateMenu() {
this.menu.innerHTML = /* HTML */ `
<div class="navbar-start">
<a id="navbar-accueil" class="navbar-item" href="#accueil">
<i class="navbar-menu-icon fas fa-home"></i>Accueil
</a>
${IsEmpty(this.user)
? ``
: /* HTML */ `
<a id="navbar-dashboard" class="navbar-item" href="#dashboard">
<i class="navbar-menu-icon fas fa-folder-open"></i>Dashboard
</a>
${this.user.isAdmin
? /* HTML */ `
<a id="navbar-sharetoken" class="navbar-item" href="#sharetoken">
<i class="navbar-menu-icon fas fa-key"></i>Share token
</a>
`
: ""}
`}
</div>
<div class="navbar-end">
${IsEmpty(this.user)
? /* HTML */ `
<a class="navbar-item" href="#login">
<i class="navbar-menu-icon fas fa-sign-in-alt"></i>Log in
</a>
`
: /* HTML */ `
<a class="navbar-item" href="/Logout">
<i class="navbar-menu-icon fas fa-sign-out-alt"></i>Log out
</a>
`}
</div>
`;
this.SetActiveItem();
}
export function SetActiveItem() {
const items = document.getElementsByClassName("navbar-item");
for (const i of items) {
i.classList.remove("is-active");
if (i.id == "navbar-" + location.hash.substring(1)) {
i.classList.add("is-active");
SetActiveItem() {
const items = document.getElementsByClassName("navbar-item");
for (const i of items) {
i.classList.remove("is-active");
if (i.id == "navbar-" + location.hash.substring(1)) {
i.classList.add("is-active");
}
}
}
}
// Imports
import { HandleError } from "/services/common/errors.js";
// DOM elements
let mountpoint;
let new_token_button;
let token_textarea;
let copy_token_button;
export async function mount(where, user) {
const shareTokenComponent = new ShareToken(user);
await shareTokenComponent.mount(where);
}
// Local variables
let current_user;
class ShareToken {
constructor(user) {
this.current_user = user;
}
export async function mount(where) {
mountpoint = where;
document.getElementById(mountpoint).innerHTML = /* HTML */ ` <div
class="container is-fluid"
>
<button id="token-new" class="button is-primary">Générer</button>
<div id="textarea-control" class="control my-2">
<textarea
id="token-textarea"
class="textarea is-info is-medium has-fixed-size"
placeholder="Click the button to generate a token"
disabled="true"
></textarea>
</div>
<button id="token-copy" class="button is-success" disabled="true">
Copier
</button>
</div>`;
new_token_button = document.getElementById("token-new");
new_token_button.addEventListener("click", function () {
generateToken();
});
token_textarea = document.getElementById("token-textarea");
copy_token_button = document.getElementById("token-copy");
copy_token_button.addEventListener("click", function () {
copy(token_textarea);
});
}
// DOM elements
new_token_button;
token_textarea;
copy_token_button;
async function generateToken() {
const control = document.getElementById("textarea-control");
control.classList.add("is-loading");
try {
const response = await fetch("/api/admin/newToken", {
method: "POST",
headers: new Headers({
"XSRF-Token": this.user.xsrftoken,
})
async mount(mountpoint) {
document.getElementById(mountpoint).innerHTML = /* HTML */ ` <div
class="container is-fluid"
>
<button id="token-new" class="button is-primary">Générer</button>
<div id="textarea-control" class="control my-2">
<textarea
id="token-textarea"
class="textarea is-info is-medium has-fixed-size"
placeholder="Click the button to generate a token"
disabled="true"
></textarea>
</div>
<button id="token-copy" class="button is-success" disabled="true">
Copier
</button>
</div>`;
this.new_token_button = document.getElementById("token-new");
this.new_token_button.addEventListener("click", async () => {
await this.generateToken();
});
this.token_textarea = document.getElementById("token-textarea");
this.copy_token_button = document.getElementById("token-copy");
this.copy_token_button.addEventListener("click", function () {
this.copy(token_textarea);
});
if (response.status !== 200) {
throw new Error(
`Le token n'a pas pu être généré (code ${response.status})`
);
}
async generateToken() {
const control = document.getElementById("textarea-control");
control.classList.add("is-loading");
try {
const response = await fetch("/api/admin/newToken", {
method: "POST",
headers: new Headers({
"XSRF-Token": this.current_user.xsrftoken,
}),
});
if (response.status !== 200) {
throw new Error(
`Le token n'a pas pu être généré (code ${response.status})`
);
}
const responseText = await response.text();
this.token_textarea.disabled = false;
this.token_textarea.value = responseText;
this.copy_token_button.disabled = false;
} catch (e) {
HandleError(e);
this.token_textarea.value = "Une erreur s'est produite !";
}
const responseText = await response.text();
token_textarea.disabled = false;
token_textarea.value = responseText;
copy_token_button.disabled = false;
} catch (e) {
HandleError(e);
token_textarea.value = "Une erreur s'est produite !";
control.classList.remove("is-loading");
}
control.classList.remove("is-loading");
}
function copy(textarea) {
textarea.select();
document.execCommand("copy");
copy_token_button.textContent = "Copié !";
copy(textarea) {
textarea.select();
document.execCommand("copy");
this.copy_token_button.textContent = "Copié !";
}
}
......@@ -7,8 +7,11 @@
<link rel="stylesheet" href="assets/bulma.min.css" />
<link rel="stylesheet" href="assets/animate.min.css" />
<link rel="icon" href="assets/icons/icon.png">