Upgrade to proper user management
This commit is contained in:
parent
93c70b0e1d
commit
02f7424b56
|
@ -5,6 +5,7 @@ import express, {Request, Response} from 'express';
|
|||
import {Guid} from 'guid-typescript';
|
||||
import logger from '../../middleware/logger';
|
||||
import {eventsRouter} from './events/events.router';
|
||||
import {usersRouter} from './users/users.router';
|
||||
|
||||
/**
|
||||
* Router Definition
|
||||
|
@ -12,6 +13,7 @@ import {eventsRouter} from './events/events.router';
|
|||
export const calendarRouter = express.Router();
|
||||
|
||||
calendarRouter.use('/events', eventsRouter);
|
||||
calendarRouter.use('/users', usersRouter);
|
||||
|
||||
|
||||
calendarRouter.get('/', async (req: Request, res: Response) => {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import * as dotenv from 'dotenv';
|
||||
import * as UserService from '../users/users.service';
|
||||
|
||||
|
||||
dotenv.config();
|
||||
|
@ -7,34 +8,48 @@ dotenv.config();
|
|||
* Checks if the password gives admin privileges (view / create / edit / delete)
|
||||
* @param password
|
||||
*/
|
||||
export const checkAdminPrivileges = (password: string) => {
|
||||
return password == process.env.ADMIN_CREDENTIAL;
|
||||
export const checkAdminPrivileges = async (sessionId: string, sessionKey: string, ip: string) => {
|
||||
if(sessionId) {
|
||||
let user = await UserService.checkSession(sessionId, sessionKey, ip);
|
||||
return user.is_active;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the password gives member view privileges
|
||||
* @param password
|
||||
*/
|
||||
export const checkMemberPrivileges = (password: string) => {
|
||||
return password == process.env.MEMBER_CREDENTIAL || password == process.env.ADMIN_CREDENTIAL;
|
||||
export const checkMemberPrivileges = async (sessionId: string, sessionKey: string, password: string, ip: string) => {
|
||||
if(sessionId) {
|
||||
let user = await UserService.checkSession(sessionId, sessionKey, ip);
|
||||
return user.is_active;
|
||||
}
|
||||
|
||||
return password == process.env.MEMBER_CREDENTIAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the password gives management view privileges
|
||||
* @param password
|
||||
*/
|
||||
export const checkManagementPrivileges = (password: string) => {
|
||||
return password == process.env.MANAGEMENT_CREDENTIAL || password == process.env.ADMIN_CREDENTIAL;
|
||||
export const checkManagementPrivileges = async (sessionId: string, sessionKey: string, password: string, ip: string) => {
|
||||
if(sessionId) {
|
||||
let user = await UserService.checkSession(sessionId, sessionKey, ip);
|
||||
return user.is_active;
|
||||
}
|
||||
|
||||
return password == process.env.MANAGEMENT_CREDENTIAL;
|
||||
}
|
||||
|
||||
export const hasAccess = (calendarName: string, password: string) => {
|
||||
export const hasAccess = async (calendarName: string, sessionId: string, sessionKey: string, password: string, ip: string) => {
|
||||
switch (calendarName) {
|
||||
case 'public':
|
||||
return true;
|
||||
case 'members':
|
||||
return checkMemberPrivileges(password);
|
||||
return await checkMemberPrivileges(sessionId, sessionKey, password, ip);
|
||||
case 'management':
|
||||
return checkManagementPrivileges(password);
|
||||
return await checkManagementPrivileges(sessionId, sessionKey, password, ip);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,8 @@ export interface Event {
|
|||
endDateTime: Date;
|
||||
createdDate: Date;
|
||||
location: string;
|
||||
createdBy: string;
|
||||
createdBy?: string;
|
||||
createdById: number,
|
||||
url: string;
|
||||
wholeDay: boolean;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import {Event} from './event.interface';
|
|||
import * as EventService from './events.service';
|
||||
import * as iCalService from './icalgenerator.service';
|
||||
import * as CredentialService from './credentials.service';
|
||||
import * as UserService from '../users/users.service';
|
||||
import {Guid} from 'guid-typescript';
|
||||
import logger from '../../../middleware/logger';
|
||||
|
||||
|
@ -35,7 +36,10 @@ eventsRouter.get('/:calendar/json', async (req: Request, res: Response) => {
|
|||
try {
|
||||
// Get request params
|
||||
let calendarName: string = req.params.calendar as string ?? '';
|
||||
let sessionId: string = req.query.sessionId as string ?? '';
|
||||
let sessionKey: string = req.query.sessionKey as string ?? '';
|
||||
let password: string = req.query.password as string ?? '';
|
||||
let ip: string = req.socket.remoteAddress ?? '';
|
||||
|
||||
if (calendarName.length < 1) {
|
||||
res.status(400).send({'message': 'Please state the name of the calendar you want events from.'});
|
||||
|
@ -49,7 +53,7 @@ eventsRouter.get('/:calendar/json', async (req: Request, res: Response) => {
|
|||
|
||||
let calendarId: number = calendarNames.get(calendarName)!.id;
|
||||
|
||||
if (!CredentialService.hasAccess(calendarName, password)) {
|
||||
if (! await CredentialService.hasAccess(calendarName, sessionId, sessionKey, password, ip)) {
|
||||
res.status(403).send({'message': 'You do not have access to the specified calendar.'});
|
||||
return;
|
||||
}
|
||||
|
@ -69,7 +73,10 @@ eventsRouter.get('/:calendar/ical', async (req: Request, res: Response) => {
|
|||
try {
|
||||
// Get request params
|
||||
let calendarName: string = req.params.calendar as string ?? '';
|
||||
let sessionId: string = req.query.sessionId as string ?? '';
|
||||
let sessionKey: string = req.query.sessionKey as string ?? '';
|
||||
let password: string = req.query.password as string ?? '';
|
||||
let ip: string = req.socket.remoteAddress ?? '';
|
||||
|
||||
if (calendarName.length < 1) {
|
||||
res.status(400).send({'message': 'Please state the name of the calendar you want events from.'});
|
||||
|
@ -83,7 +90,7 @@ eventsRouter.get('/:calendar/ical', async (req: Request, res: Response) => {
|
|||
|
||||
let calendarId: number = calendarNames.get(calendarName)!.id;
|
||||
|
||||
if (!CredentialService.hasAccess(calendarName, password)) {
|
||||
if (! await CredentialService.hasAccess(calendarName, sessionId, sessionKey, password, ip)) {
|
||||
res.status(403).send({'message': 'You do not have access to the specified calendar.'});
|
||||
return;
|
||||
}
|
||||
|
@ -112,10 +119,14 @@ eventsRouter.get('/:calendar/ical', async (req: Request, res: Response) => {
|
|||
eventsRouter.post('/', async (req: Request, res: Response) => {
|
||||
try {
|
||||
// Get params
|
||||
let password = req.body.password;
|
||||
let sessionId: string = req.query.sessionId as string ?? '';
|
||||
let sessionKey: string = req.query.sessionKey as string ?? '';
|
||||
let ip: string = req.socket.remoteAddress ?? '';
|
||||
|
||||
if (!CredentialService.checkAdminPrivileges(password)) {
|
||||
res.status(403).send({'message': 'Insufficient privileges.'});
|
||||
let user = await UserService.checkSession(sessionId, sessionKey, ip);
|
||||
|
||||
if (!user.is_active) {
|
||||
res.status(403).send({'message': 'You do not have access to the specified calendar.'});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -123,8 +134,7 @@ eventsRouter.post('/', async (req: Request, res: Response) => {
|
|||
req.body.calendarId === undefined ||
|
||||
isNullOrBlank(req.body.name) ||
|
||||
req.body.startDateTime === undefined ||
|
||||
req.body.endDateTime === undefined ||
|
||||
isNullOrBlank(req.body.createdBy)
|
||||
req.body.endDateTime === undefined
|
||||
) {
|
||||
res.status(400).send({'message': 'Required parameters missing'});
|
||||
return;
|
||||
|
@ -140,7 +150,7 @@ eventsRouter.post('/', async (req: Request, res: Response) => {
|
|||
endDateTime: new Date(req.body.endDateTime),
|
||||
createdDate: new Date(),
|
||||
location: req.body.location ?? '',
|
||||
createdBy: req.body.createdBy ?? '',
|
||||
createdById: user.user_id ?? -1,
|
||||
url: req.body.url ?? '',
|
||||
wholeDay: req.body.wholeDay ?? false
|
||||
};
|
||||
|
@ -165,10 +175,14 @@ eventsRouter.post('/', async (req: Request, res: Response) => {
|
|||
eventsRouter.put('/:eventId', async (req: Request, res: Response) => {
|
||||
try {
|
||||
// Get params
|
||||
let password = req.body.password;
|
||||
let sessionId: string = req.query.sessionId as string ?? '';
|
||||
let sessionKey: string = req.query.sessionKey as string ?? '';
|
||||
let ip: string = req.socket.remoteAddress ?? '';
|
||||
|
||||
if (!CredentialService.checkAdminPrivileges(password)) {
|
||||
res.status(403).send({'message': 'Insufficient privileges.'});
|
||||
let user = await UserService.checkSession(sessionId, sessionKey, ip);
|
||||
|
||||
if (!user.is_active) {
|
||||
res.status(403).send({'message': 'You do not have access to the specified calendar.'});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -177,8 +191,7 @@ eventsRouter.put('/:eventId', async (req: Request, res: Response) => {
|
|||
req.body.calendarId === undefined ||
|
||||
isNullOrBlank(req.body.name) ||
|
||||
req.body.startDateTime === undefined ||
|
||||
req.body.endDateTime === undefined ||
|
||||
isNullOrBlank(req.body.createdBy)
|
||||
req.body.endDateTime === undefined
|
||||
) {
|
||||
res.status(400).send({'message': 'Required parameters missing'});
|
||||
return;
|
||||
|
@ -195,6 +208,7 @@ eventsRouter.put('/:eventId', async (req: Request, res: Response) => {
|
|||
createdDate: new Date(),
|
||||
location: req.body.location ?? '',
|
||||
createdBy: req.body.createdBy ?? '',
|
||||
createdById: user.user_id ?? -1,
|
||||
url: req.body.url ?? '',
|
||||
wholeDay: req.body.wholeDay ?? false
|
||||
};
|
||||
|
@ -222,10 +236,14 @@ eventsRouter.put('/:eventId', async (req: Request, res: Response) => {
|
|||
eventsRouter.delete('/:eventId', async (req: Request, res: Response) => {
|
||||
try {
|
||||
// Get params
|
||||
let password = req.body.password;
|
||||
let sessionId: string = req.query.sessionId as string ?? '';
|
||||
let sessionKey: string = req.query.sessionKey as string ?? '';
|
||||
let ip: string = req.socket.remoteAddress ?? '';
|
||||
|
||||
if (!CredentialService.checkAdminPrivileges(password)) {
|
||||
res.status(403).send({'message': 'Insufficient privileges.'});
|
||||
let user = await UserService.checkSession(sessionId, sessionKey, ip);
|
||||
|
||||
if (!user.is_active) {
|
||||
res.status(403).send({'message': 'You do not have access to the specified calendar.'});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -247,6 +265,7 @@ eventsRouter.delete('/:eventId', async (req: Request, res: Response) => {
|
|||
createdDate: new Date(),
|
||||
location: '',
|
||||
createdBy: '',
|
||||
createdById: -1,
|
||||
url: '',
|
||||
wholeDay: false
|
||||
};
|
||||
|
|
|
@ -14,7 +14,7 @@ export const getAllEvents = async (calendarId: number): Promise<Event[]> => {
|
|||
let conn = await NachklangCalendarDB.getConnection();
|
||||
let eventRows: Event[] = [];
|
||||
try {
|
||||
const eventsQuery = 'SELECT * FROM events WHERE calendar_id = ?';
|
||||
const eventsQuery = 'SELECT e.*, u.full_name FROM events e LEFT OUTER JOIN users u ON e.created_by_id = u.user_id WHERE calendar_id = ?';
|
||||
const eventsRes = await conn.query(eventsQuery, calendarId);
|
||||
|
||||
for (let row of eventsRes) {
|
||||
|
@ -28,7 +28,8 @@ export const getAllEvents = async (calendarId: number): Promise<Event[]> => {
|
|||
endDateTime: row.end_datetime,
|
||||
createdDate: row.created_date,
|
||||
location: row.location,
|
||||
createdBy: row.created_by,
|
||||
createdBy: row.full_name,
|
||||
createdById: row.created_by_id,
|
||||
url: row.url,
|
||||
wholeDay: row.whole_day
|
||||
});
|
||||
|
@ -51,8 +52,8 @@ export const createEvent = async (event: Event): Promise<number> => {
|
|||
let conn = await NachklangCalendarDB.getConnection();
|
||||
try {
|
||||
let eventUUID = Guid.create().toString();
|
||||
const eventsQuery = 'INSERT INTO events (calendar_id, uuid, name, description, start_datetime, end_datetime, location, created_by, url, whole_day) VALUES (?,?,?,?,?,?,?,?,?,?) RETURNING event_id';
|
||||
const eventsRes = await conn.execute(eventsQuery, [event.calendarId, eventUUID, event.name, event.description, event.startDateTime, event.endDateTime, event.location, event.createdBy, event.url, event.wholeDay]);
|
||||
const eventsQuery = 'INSERT INTO events (calendar_id, uuid, name, description, start_datetime, end_datetime, location, created_by_id, url, whole_day) VALUES (?,?,?,?,?,?,?,?,?,?) RETURNING event_id';
|
||||
const eventsRes = await conn.execute(eventsQuery, [event.calendarId, eventUUID, event.name, event.description, event.startDateTime, event.endDateTime, event.location, event.createdById, event.url, event.wholeDay]);
|
||||
|
||||
return eventsRes[0].event_id;
|
||||
} catch (err) {
|
||||
|
@ -72,8 +73,8 @@ export const createEvent = async (event: Event): Promise<number> => {
|
|||
export const updateEvent = async (event: Event): Promise<number> => {
|
||||
let conn = await NachklangCalendarDB.getConnection();
|
||||
try {
|
||||
const eventsQuery = 'UPDATE events SET name = ?, description = ?, start_datetime = ?, end_datetime = ?, location = ?, created_by = ?, url = ?, whole_day = ? WHERE event_id = ?';
|
||||
const eventsRes = await conn.execute(eventsQuery, [event.name, event.description, event.startDateTime, event.endDateTime, event.location, event.createdBy, event.url, event.wholeDay, event.eventId]);
|
||||
const eventsQuery = 'UPDATE events SET name = ?, description = ?, start_datetime = ?, end_datetime = ?, location = ?, created_by_id = ?, url = ?, whole_day = ? WHERE event_id = ?';
|
||||
const eventsRes = await conn.execute(eventsQuery, [event.name, event.description, event.startDateTime, event.endDateTime, event.location, event.createdById, event.url, event.wholeDay, event.eventId]);
|
||||
|
||||
return eventsRes.affectedRows;
|
||||
} catch (err) {
|
||||
|
|
9
src/models/calendar/users/session.interface.ts
Normal file
9
src/models/calendar/users/session.interface.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
export interface Session {
|
||||
session_id: number;
|
||||
user_id: number;
|
||||
session_key: string;
|
||||
session_key_hash: string;
|
||||
created_date?: Date;
|
||||
valid_until?: Date;
|
||||
last_ip: string;
|
||||
}
|
7
src/models/calendar/users/user.interface.ts
Normal file
7
src/models/calendar/users/user.interface.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
export interface User {
|
||||
user_id: number;
|
||||
full_name: string;
|
||||
password_hash: string;
|
||||
email: string;
|
||||
is_active: boolean;
|
||||
}
|
125
src/models/calendar/users/users.router.ts
Normal file
125
src/models/calendar/users/users.router.ts
Normal file
|
@ -0,0 +1,125 @@
|
|||
/**
|
||||
* Required External Modules and Interfaces
|
||||
*/
|
||||
|
||||
import express, {Request, Response} from 'express';
|
||||
import * as UserService from './users.service';
|
||||
import {Session} from './session.interface';
|
||||
import {User} from './user.interface';
|
||||
import {Guid} from 'guid-typescript';
|
||||
import logger from '../../../middleware/logger';
|
||||
|
||||
/**
|
||||
* Router Definition
|
||||
*/
|
||||
|
||||
export const usersRouter = express.Router();
|
||||
|
||||
|
||||
/**
|
||||
* Controller Definitions
|
||||
*/
|
||||
|
||||
// POST users/register
|
||||
usersRouter.post('/register', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const password: string = req.body.password;
|
||||
const email: string = req.body.email;
|
||||
const fullName: string = req.body.fullName;
|
||||
const ip: string = req.socket.remoteAddress ?? '';
|
||||
|
||||
if (!password || !email) {
|
||||
// Missing
|
||||
res.status(400).send(JSON.stringify({message: 'Missing parameters'}));
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the user and a session
|
||||
const session: Session = await UserService.createUser(email, password, fullName, ip);
|
||||
|
||||
// Send the session details back to the user
|
||||
res.status(201).send({
|
||||
session_id: session.session_id,
|
||||
session_key: session.session_key
|
||||
});
|
||||
} catch (e: any) {
|
||||
let errorGuid = Guid.create().toString();
|
||||
logger.error('Error handling a request: ' + e.message, {reference: errorGuid});
|
||||
res.status(500).send({
|
||||
'status': 'PROCESSING_ERROR',
|
||||
'message': 'Internal Server Error. Try again later.',
|
||||
'reference': errorGuid
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// POST users/login
|
||||
usersRouter.post('/login', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const password: string = req.body.password;
|
||||
const email: string = req.body.email;
|
||||
const ip: string = req.socket.remoteAddress ?? '';
|
||||
|
||||
if (!password || !email) {
|
||||
// Missing
|
||||
res.status(400).send(JSON.stringify({message: 'Missing parameters'}));
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a session
|
||||
const session: Session = await UserService.login(email, password, ip);
|
||||
|
||||
if (!session.session_id) {
|
||||
// Error logging in, probably wrong username / password
|
||||
res.status(401).send(JSON.stringify({messages: ['Wrong username and / or password']}));
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the session details back to the user
|
||||
res.status(200).send({
|
||||
session_id: session.session_id,
|
||||
session_key: session.session_key
|
||||
});
|
||||
} catch (e: any) {
|
||||
let errorGuid = Guid.create().toString();
|
||||
logger.error('Error handling a request: ' + e.message, {reference: errorGuid});
|
||||
res.status(500).send({
|
||||
'status': 'PROCESSING_ERROR',
|
||||
'message': 'Internal Server Error. Try again later.',
|
||||
'reference': errorGuid
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// POST users/checkSessionValid
|
||||
usersRouter.post('/checkSessionValid', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const ip: string = req.socket.remoteAddress ?? '';
|
||||
const session_id = req.body.session_id;
|
||||
const session_key = req.body.session_key;
|
||||
|
||||
if (!session_id || !session_key) {
|
||||
// Error logging in, probably wrong username / password
|
||||
res.status(401).send(JSON.stringify({messages: ['No session detected']}));
|
||||
return;
|
||||
}
|
||||
|
||||
const user: User = await UserService.checkSession(session_id, session_key, ip);
|
||||
|
||||
if (!user.user_id) {
|
||||
// Error logging in, probably wrong username / password
|
||||
res.status(401).send(JSON.stringify({messages: ['Invalid session']}));
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(200).send(user);
|
||||
} catch (e: any) {
|
||||
let errorGuid = Guid.create().toString();
|
||||
logger.error('Error handling a request: ' + e.message, {reference: errorGuid});
|
||||
res.status(500).send({
|
||||
'status': 'PROCESSING_ERROR',
|
||||
'message': 'Internal Server Error. Try again later.',
|
||||
'reference': errorGuid
|
||||
});
|
||||
}
|
||||
});
|
182
src/models/calendar/users/users.service.ts
Normal file
182
src/models/calendar/users/users.service.ts
Normal file
|
@ -0,0 +1,182 @@
|
|||
import * as dotenv from 'dotenv';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
import {Guid} from 'guid-typescript';
|
||||
import {User} from './user.interface';
|
||||
import {Session} from './session.interface';
|
||||
import {NachklangCalendarDB} from '../Calendar.db';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
/**
|
||||
* Data Model Interfaces
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Service Methods
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a user record in the database, also creates a session. Returns the session if successful.
|
||||
*/
|
||||
export const createUser = async (email: string, password: string, fullName: string, ip: string): Promise<Session> => {
|
||||
let conn = await NachklangCalendarDB.getConnection();
|
||||
try {
|
||||
// Hash password and generate + hash session key
|
||||
const pwHash = bcrypt.hashSync(password, 10);
|
||||
const sessionKey = Guid.create().toString();
|
||||
const sessionKeyHash = bcrypt.hashSync(sessionKey, 10);
|
||||
|
||||
// Create user entry in SQL
|
||||
const userQuery = 'INSERT INTO users (email, password_hash, full_name) VALUES (?, ?, ?) RETURNING user_id';
|
||||
const userIdRes = await conn.query(userQuery, [email, pwHash, fullName]);
|
||||
|
||||
// Get user id of the created user
|
||||
let userId: number = -1;
|
||||
for (const row of userIdRes) {
|
||||
userId = row.user_id;
|
||||
}
|
||||
|
||||
// Create session
|
||||
const sessionQuery = 'INSERT INTO sessions (user_id, session_key_hash, created_date, valid_until, last_ip) VALUES (?,?,NOW(),DATE_ADD(NOW(), INTERVAL 30 DAY),?) RETURNING session_id';
|
||||
const sessionIdRes = await conn.query(sessionQuery, [userId, sessionKeyHash, ip]);
|
||||
await conn.commit();
|
||||
|
||||
// Get session id of the created session
|
||||
let sessionId: number = -1;
|
||||
for (const row of sessionIdRes) {
|
||||
sessionId = row.session_id;
|
||||
}
|
||||
|
||||
return {
|
||||
session_id: sessionId,
|
||||
user_id: userId,
|
||||
session_key: sessionKey,
|
||||
session_key_hash: 'HIDDEN',
|
||||
last_ip: ip
|
||||
};
|
||||
} catch (err) {
|
||||
throw err;
|
||||
} finally {
|
||||
// Return connection
|
||||
await conn.end();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the given credentials are valid and creates a new session if they are.
|
||||
* Returns the session information in case of a successful login
|
||||
*/
|
||||
export const login = async (email: string, password: string, ip: string): Promise<Session> => {
|
||||
let conn = await NachklangCalendarDB.getConnection();
|
||||
try {
|
||||
// Get saved password hash
|
||||
const query = 'SELECT user_id, password_hash FROM users WHERE email = ?';
|
||||
const userRows = await conn.query(query, email);
|
||||
let savedHash = '';
|
||||
let userId = -1;
|
||||
for (const row of userRows) {
|
||||
savedHash = row.password_hash;
|
||||
userId = row.user_id;
|
||||
}
|
||||
|
||||
// Check for correct password
|
||||
if (!bcrypt.compareSync(password, savedHash)) {
|
||||
// Wrong password, return invalid
|
||||
return {} as Session;
|
||||
}
|
||||
|
||||
// Generate + hash session key
|
||||
const sessionKey = Guid.create().toString();
|
||||
const sessionKeyHash = bcrypt.hashSync(sessionKey, 10);
|
||||
|
||||
// Create session
|
||||
const sessionQuery = 'INSERT INTO sessions (user_id, session_key_hash, created_date, valid_until, last_ip) VALUES (?,?,NOW(),DATE_ADD(NOW(), INTERVAL 30 DAY),?) RETURNING session_id';
|
||||
const sessionIdRes = await conn.query(sessionQuery, [userId, sessionKeyHash, ip]);
|
||||
await conn.commit();
|
||||
|
||||
// Get session id of the created session
|
||||
let sessionId: number = -1;
|
||||
for (const row of sessionIdRes) {
|
||||
sessionId = row.session_id;
|
||||
}
|
||||
|
||||
return {
|
||||
session_id: sessionId,
|
||||
user_id: userId,
|
||||
session_key: sessionKey,
|
||||
session_key_hash: 'HIDDEN',
|
||||
last_ip: ip
|
||||
};
|
||||
} catch (err) {
|
||||
throw err;
|
||||
} finally {
|
||||
// Return connection
|
||||
await conn.end();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the given session information are valid and returns the user information if they are
|
||||
*/
|
||||
export const checkSession = async (sessionId: string, sessionKey: string, ip: string): Promise<User> => {
|
||||
let conn = await NachklangCalendarDB.getConnection();
|
||||
try {
|
||||
// Get saved session key hash
|
||||
const query = 'SELECT user_id, session_key_hash, valid_until FROM sessions WHERE session_id = ?';
|
||||
const sessionRows = await conn.query(query, sessionId);
|
||||
let savedHash = '';
|
||||
let userId = -1;
|
||||
let validUntil = new Date();
|
||||
for (const row of sessionRows) {
|
||||
savedHash = row.session_key_hash;
|
||||
userId = row.user_id;
|
||||
validUntil = row.valid_until;
|
||||
}
|
||||
|
||||
// Check for correct key
|
||||
if (!bcrypt.compareSync(sessionKey, savedHash)) {
|
||||
// Wrong key, return invalid
|
||||
return {} as User;
|
||||
}
|
||||
|
||||
// Check if the session is still valid
|
||||
if (validUntil <= new Date()) {
|
||||
// Session expired, return invalid
|
||||
return {} as User;
|
||||
}
|
||||
|
||||
// Update session entry in SQL
|
||||
const updateSessionsQuery = 'UPDATE sessions SET last_IP = ? WHERE session_id = ?';
|
||||
const userIdRes = await conn.query(updateSessionsQuery, [ip, sessionId]);
|
||||
await conn.commit();
|
||||
|
||||
// Get the other required user information
|
||||
const userQuery = 'SELECT user_id, email, full_name, is_active FROM users WHERE user_id = ?';
|
||||
const userRows = await conn.query(userQuery, userId);
|
||||
let username = '';
|
||||
let email = '';
|
||||
let fullName = '';
|
||||
let is_active = false;
|
||||
for (const row of userRows) {
|
||||
username = row.username;
|
||||
email = row.email;
|
||||
fullName = row.full_name;
|
||||
is_active = row.is_active;
|
||||
}
|
||||
|
||||
// Everything is fine, return user information
|
||||
return {
|
||||
user_id: userId,
|
||||
email: email,
|
||||
password_hash: 'HIDDEN',
|
||||
full_name: fullName,
|
||||
is_active: is_active
|
||||
};
|
||||
} catch (err) {
|
||||
throw err;
|
||||
} finally {
|
||||
// Return connection
|
||||
await conn.end();
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue
Block a user