Add session manager ui

This commit is contained in:
KernelDeimos 2024-04-11 00:29:39 -04:00
parent 18b3e06fe8
commit 09bf422686
7 changed files with 270 additions and 4 deletions

View File

@ -90,7 +90,7 @@ router.post('/login', express.json(), body_parser_error_handler, async (req, res
// check password
if(await bcrypt.compare(req.body.password, user.password)){
const svc_auth = req.services.get('auth');
const token = await svc_auth.create_session_token(user);
const token = await svc_auth.create_session_token(user, { req });
//set cookie
// res.cookie(config.cookie_name, token);
res.cookie(config.cookie_name, token, {

View File

@ -247,7 +247,9 @@ module.exports = eggspress(['/signup'], {
);
// create token for login
const token = await svc_auth.create_session_token(user);
const token = await svc_auth.create_session_token(user, {
req,
});
// jwt.sign({uuid: user_uuid}, config.jwt_secret);
//-------------------------------------------------------------

View File

@ -174,6 +174,44 @@ class AuthService extends BaseService {
async create_session_ (user, meta = {}) {
this.log.info(`CREATING SESSION`);
if ( meta.req ) {
const req = meta.req;
delete meta.req;
const ip = this.global_config.fowarded
? req.headers['x-forwarded-for'] ||
req.connection.remoteAddress
: req.connection.remoteAddress
;
meta.ip = ip;
meta.server = this.global_config.server_id;
if ( req.headers['user-agent'] ) {
meta.user_agent = req.headers['user-agent'];
}
if ( req.headers['referer'] ) {
meta.referer = req.headers['referer'];
}
if ( req.headers['origin'] ) {
const origin = this._origin_from_url(req.headers['origin']);
if ( origin ) {
meta.origin = origin;
}
}
if ( req.headers['host'] ) {
const host = this._origin_from_url(req.headers['host']);
if ( host ) {
meta.host = host;
}
}
}
const uuid = this.modules.uuidv4();
await this.db.write(
'INSERT INTO `sessions` ' +
@ -197,6 +235,8 @@ class AuthService extends BaseService {
[uuid],
);
session.meta = JSON.parse(session.meta ?? {});
return session;
}
@ -214,7 +254,7 @@ class AuthService extends BaseService {
return token;
}
async check_session (cur_token) {
async check_session (cur_token, meta) {
const decoded = this.modules.jwt.verify(
cur_token, this.global_config.jwt_secret
);
@ -245,7 +285,7 @@ class AuthService extends BaseService {
// Upgrade legacy token
// TODO: phase this out
const { token } = await this.create_session_token(user);
const { token } = await this.create_session_token(user, meta);
return { user, token };
}
@ -318,6 +358,7 @@ class AuthService extends BaseService {
if ( session.uuid === actor.type.session ) {
session.current = true;
}
session.meta = JSON.parse(session.meta ?? {});
});
return sessions;

View File

@ -26,6 +26,7 @@ import changeLanguage from "../../i18n/i18nChangeLanguage.js"
import UIWindowConfirmUserDeletion from './UIWindowConfirmUserDeletion.js';
import UITabAbout from './UITabAbout.js';
import UIWindowThemeDialog from '../UIWindowThemeDialog.js';
import UIWindowManageSessions from '../UIWindowManageSessions.js';
async function UIWindowSettings(options){
return new Promise(async (resolve) => {
@ -111,6 +112,14 @@ async function UIWindowSettings(options){
h += `</div>`;
h += `</div>`;
// session manager
h += `<div class="settings-card">`;
h += `<strong>${i18n('sessions')}</strong>`;
h += `<div style="flex-grow:1;">`;
h += `<button class="button manage-sessions" style="float:right;">${i18n('manage_sessions')}</button>`;
h += `</div>`;
h += `</div>`;
h += `</div>`;
// Personalization
@ -324,6 +333,10 @@ async function UIWindowSettings(options){
UIWindowThemeDialog();
})
$(el_window).find('.manage-sessions').on('click', function (e) {
UIWindowManageSessions();
})
$(el_window).on('click', '.settings-sidebar-item', function(){
const $this = $(this);
const settings = $this.attr('data-settings');

View File

@ -0,0 +1,148 @@
import UIAlert from "./UIAlert.js";
import UIWindow from "./UIWindow.js";
const UIWindowManageSessions = async function UIWindowManageSessions () {
const services = globalThis.services;
const w = await UIWindow({
title: i18n('ui_manage_sessions'),
icon: null,
uid: null,
is_dir: false,
message: 'message',
// body_icon: options.body_icon,
// backdrop: options.backdrop ?? false,
is_droppable: false,
has_head: true,
selectable_body: false,
draggable_body: true,
allow_context_menu: false,
window_class: 'window-session-manager',
dominant: true,
body_content: '',
// width: 600,
// parent_uuid: options.parent_uuid,
// ...options.window_options,
});
const SessionWidget = ({ session }) => {
const el = document.createElement('div');
el.classList.add('session-widget');
el.dataset.uuid = session.uuid;
// '<pre>' +
// JSON.stringify(session, null, 2) +
// '</pre>';
const el_uuid = document.createElement('div');
el_uuid.textContent = session.uuid;
el.appendChild(el_uuid);
el_uuid.classList.add('session-widget-uuid');
const el_meta = document.createElement('div');
el_meta.classList.add('session-widget-meta');
for ( const key in session.meta ) {
const el_entry = document.createElement('div');
el_entry.classList.add('session-widget-meta-entry');
const el_key = document.createElement('div');
el_key.textContent = key;
el_key.classList.add('session-widget-meta-key');
el_entry.appendChild(el_key);
const el_value = document.createElement('div');
el_value.textContent = session.meta[key];
el_value.classList.add('session-widget-meta-value');
el_entry.appendChild(el_value);
el_meta.appendChild(el_entry);
}
el.appendChild(el_meta);
const el_actions = document.createElement('div');
el_actions.classList.add('session-widget-actions');
const el_btn_revoke = document.createElement('button');
el_btn_revoke.textContent = i18n('ui_revoke');
el_btn_revoke.classList.add('button', 'button-danger');
el_btn_revoke.addEventListener('click', async () => {
try{
const alert_resp = await UIAlert({
message: i18n('confirm_session_revoke'),
buttons:[
{
label: i18n('yes'),
value: 'yes',
type: 'primary',
},
{
label: i18n('cancel')
},
]
});
if ( alert_resp !== 'yes' ) {
return;
}
const resp = await fetch(`${api_origin}/auth/revoke-session`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
uuid: session.uuid,
}),
});
if ( resp.ok ) {
el.remove();
return;
}
UIAlert({ message: await resp.text() }).appendTo(w_body);
} catch ( e ) {
UIAlert({ message: e.toString() }).appendTo(w_body);
}
});
el_actions.appendChild(el_btn_revoke);
el.appendChild(el_actions);
return {
appendTo (parent) {
parent.appendChild(el);
return this;
}
};
};
const reload_sessions = async () => {
const resp = await fetch(`${api_origin}/auth/list-sessions`, {
method: 'GET',
});
const sessions = await resp.json();
for ( const el of w_body.querySelectorAll('.session-widget') ) {
if ( !sessions.find(s => s.uuid === el.dataset.uuid) ) {
el.remove();
}
}
for ( const session of sessions ) {
if ( w.querySelector(`.session-widget[data-uuid="${session.uuid}"]`) ) {
continue;
}
SessionWidget({ session }).appendTo(w_body);
}
};
const w_body = w.querySelector('.window-body');
w_body.classList.add('session-manager-list');
reload_sessions();
const interval = setInterval(reload_sessions, 8000);
w.on_close = () => {
clearInterval(interval);
}
};
export default UIWindowManageSessions;

View File

@ -3703,3 +3703,61 @@ label {
background: #04AA6D;
cursor: pointer;
}
.session-manager-list {
display: flex;
flex-direction: column;
gap: 10px;
padding: 10px;
box-sizing: border-box;
height: 100% !important;
}
.session-widget {
display: flex;
flex-direction: column;
padding: 10px;
border: 1px solid #e0e0e0;
border-radius: 4px;
gap: 4px;
}
.session-widget-uuid {
font-size: 12px;
font-weight: 600;
color: #9c185b;
}
.session-widget-meta {
display: flex;
flex-direction: column;
gap: 10px;
max-height: 100px;
overflow-y: scroll;
}
.session-widget-meta-entry {
display: flex;
flex-direction: row;
align-items: center;
}
.session-widget-meta-key {
font-size: 12px;
color: #666;
flex-basis: 40%;
flex-shrink: 0;
}
.session-widget-meta-value {
font-size: 12px;
color: #666;
flex-grow: 1;
}
.session-widget-actions {
display: flex;
flex-direction: row;
gap: 10px;
justify-content: flex-end;
}

View File

@ -51,6 +51,7 @@ const en = {
confirm_new_password: "Confirm New Password",
confirm_delete_user: "Are you sure you want to delete your account? All your files and data will be permanently deleted. This action cannot be undone.",
confirm_delete_user_title: "Delete Account?",
confirm_session_revoke: "Are you sure you want to revoke this session?",
contact_us: "Contact Us",
contain: 'Contain',
continue: "Continue",
@ -112,6 +113,7 @@ const en = {
log_in: "Log In",
log_into_another_account_anyway: 'Log into another account anyway',
log_out: 'Log Out',
manage_sessions: "Manage Sessions",
move: 'Move',
moving: "Moving",
my_websites: "My Websites",
@ -205,6 +207,8 @@ const en = {
type: 'Type',
type_confirm_to_delete_account: "Type 'confirm' to delete your account.",
ui_colors: "UI Colors",
ui_manage_sessions: "Session Manager",
ui_revoke: "Revoke",
undo: 'Undo',
unlimited: 'Unlimited',
unzip: "Unzip",