Compare commits
2 Commits
93c70b0e1d
...
95983021ed
Author | SHA1 | Date | |
---|---|---|---|
95983021ed | |||
02f7424b56 |
|
@ -5,6 +5,7 @@ import express, {Request, Response} from 'express';
|
||||||
import {Guid} from 'guid-typescript';
|
import {Guid} from 'guid-typescript';
|
||||||
import logger from '../../middleware/logger';
|
import logger from '../../middleware/logger';
|
||||||
import {eventsRouter} from './events/events.router';
|
import {eventsRouter} from './events/events.router';
|
||||||
|
import {usersRouter} from './users/users.router';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Router Definition
|
* Router Definition
|
||||||
|
@ -12,6 +13,7 @@ import {eventsRouter} from './events/events.router';
|
||||||
export const calendarRouter = express.Router();
|
export const calendarRouter = express.Router();
|
||||||
|
|
||||||
calendarRouter.use('/events', eventsRouter);
|
calendarRouter.use('/events', eventsRouter);
|
||||||
|
calendarRouter.use('/users', usersRouter);
|
||||||
|
|
||||||
|
|
||||||
calendarRouter.get('/', async (req: Request, res: Response) => {
|
calendarRouter.get('/', async (req: Request, res: Response) => {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import * as dotenv from 'dotenv';
|
import * as dotenv from 'dotenv';
|
||||||
|
import * as UserService from '../users/users.service';
|
||||||
|
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
@ -7,34 +8,48 @@ dotenv.config();
|
||||||
* Checks if the password gives admin privileges (view / create / edit / delete)
|
* Checks if the password gives admin privileges (view / create / edit / delete)
|
||||||
* @param password
|
* @param password
|
||||||
*/
|
*/
|
||||||
export const checkAdminPrivileges = (password: string) => {
|
export const checkAdminPrivileges = async (sessionId: string, sessionKey: string, ip: string) => {
|
||||||
return password == process.env.ADMIN_CREDENTIAL;
|
if(sessionId) {
|
||||||
|
let user = await UserService.checkSession(sessionId, sessionKey, ip);
|
||||||
|
return user.isActive;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the password gives member view privileges
|
* Checks if the password gives member view privileges
|
||||||
* @param password
|
* @param password
|
||||||
*/
|
*/
|
||||||
export const checkMemberPrivileges = (password: string) => {
|
export const checkMemberPrivileges = async (sessionId: string, sessionKey: string, password: string, ip: string) => {
|
||||||
return password == process.env.MEMBER_CREDENTIAL || password == process.env.ADMIN_CREDENTIAL;
|
if(sessionId) {
|
||||||
|
let user = await UserService.checkSession(sessionId, sessionKey, ip);
|
||||||
|
return user.isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
return password == process.env.MEMBER_CREDENTIAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the password gives management view privileges
|
* Checks if the password gives management view privileges
|
||||||
* @param password
|
* @param password
|
||||||
*/
|
*/
|
||||||
export const checkManagementPrivileges = (password: string) => {
|
export const checkManagementPrivileges = async (sessionId: string, sessionKey: string, password: string, ip: string) => {
|
||||||
return password == process.env.MANAGEMENT_CREDENTIAL || password == process.env.ADMIN_CREDENTIAL;
|
if(sessionId) {
|
||||||
|
let user = await UserService.checkSession(sessionId, sessionKey, ip);
|
||||||
|
return user.isActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const hasAccess = (calendarName: string, password: string) => {
|
return password == process.env.MANAGEMENT_CREDENTIAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const hasAccess = async (calendarName: string, sessionId: string, sessionKey: string, password: string, ip: string) => {
|
||||||
switch (calendarName) {
|
switch (calendarName) {
|
||||||
case 'public':
|
case 'public':
|
||||||
return true;
|
return true;
|
||||||
case 'members':
|
case 'members':
|
||||||
return checkMemberPrivileges(password);
|
return await checkMemberPrivileges(sessionId, sessionKey, password, ip);
|
||||||
case 'management':
|
case 'management':
|
||||||
return checkManagementPrivileges(password);
|
return await checkManagementPrivileges(sessionId, sessionKey, password, ip);
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,8 @@ export interface Event {
|
||||||
endDateTime: Date;
|
endDateTime: Date;
|
||||||
createdDate: Date;
|
createdDate: Date;
|
||||||
location: string;
|
location: string;
|
||||||
createdBy: string;
|
createdBy?: string;
|
||||||
|
createdById: number,
|
||||||
url: string;
|
url: string;
|
||||||
wholeDay: boolean;
|
wholeDay: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {Event} from './event.interface';
|
||||||
import * as EventService from './events.service';
|
import * as EventService from './events.service';
|
||||||
import * as iCalService from './icalgenerator.service';
|
import * as iCalService from './icalgenerator.service';
|
||||||
import * as CredentialService from './credentials.service';
|
import * as CredentialService from './credentials.service';
|
||||||
|
import * as UserService from '../users/users.service';
|
||||||
import {Guid} from 'guid-typescript';
|
import {Guid} from 'guid-typescript';
|
||||||
import logger from '../../../middleware/logger';
|
import logger from '../../../middleware/logger';
|
||||||
|
|
||||||
|
@ -35,7 +36,10 @@ eventsRouter.get('/:calendar/json', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
// Get request params
|
// Get request params
|
||||||
let calendarName: string = req.params.calendar as string ?? '';
|
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 password: string = req.query.password as string ?? '';
|
||||||
|
let ip: string = req.socket.remoteAddress ?? '';
|
||||||
|
|
||||||
if (calendarName.length < 1) {
|
if (calendarName.length < 1) {
|
||||||
res.status(400).send({'message': 'Please state the name of the calendar you want events from.'});
|
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;
|
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.'});
|
res.status(403).send({'message': 'You do not have access to the specified calendar.'});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -69,7 +73,10 @@ eventsRouter.get('/:calendar/ical', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
// Get request params
|
// Get request params
|
||||||
let calendarName: string = req.params.calendar as string ?? '';
|
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 password: string = req.query.password as string ?? '';
|
||||||
|
let ip: string = req.socket.remoteAddress ?? '';
|
||||||
|
|
||||||
if (calendarName.length < 1) {
|
if (calendarName.length < 1) {
|
||||||
res.status(400).send({'message': 'Please state the name of the calendar you want events from.'});
|
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;
|
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.'});
|
res.status(403).send({'message': 'You do not have access to the specified calendar.'});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -112,10 +119,14 @@ eventsRouter.get('/:calendar/ical', async (req: Request, res: Response) => {
|
||||||
eventsRouter.post('/', async (req: Request, res: Response) => {
|
eventsRouter.post('/', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
// Get params
|
// 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)) {
|
let user = await UserService.checkSession(sessionId, sessionKey, ip);
|
||||||
res.status(403).send({'message': 'Insufficient privileges.'});
|
|
||||||
|
if (!user.isActive) {
|
||||||
|
res.status(403).send({'message': 'You do not have access to the specified calendar.'});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,8 +134,7 @@ eventsRouter.post('/', async (req: Request, res: Response) => {
|
||||||
req.body.calendarId === undefined ||
|
req.body.calendarId === undefined ||
|
||||||
isNullOrBlank(req.body.name) ||
|
isNullOrBlank(req.body.name) ||
|
||||||
req.body.startDateTime === undefined ||
|
req.body.startDateTime === undefined ||
|
||||||
req.body.endDateTime === undefined ||
|
req.body.endDateTime === undefined
|
||||||
isNullOrBlank(req.body.createdBy)
|
|
||||||
) {
|
) {
|
||||||
res.status(400).send({'message': 'Required parameters missing'});
|
res.status(400).send({'message': 'Required parameters missing'});
|
||||||
return;
|
return;
|
||||||
|
@ -140,7 +150,7 @@ eventsRouter.post('/', async (req: Request, res: Response) => {
|
||||||
endDateTime: new Date(req.body.endDateTime),
|
endDateTime: new Date(req.body.endDateTime),
|
||||||
createdDate: new Date(),
|
createdDate: new Date(),
|
||||||
location: req.body.location ?? '',
|
location: req.body.location ?? '',
|
||||||
createdBy: req.body.createdBy ?? '',
|
createdById: user.userId ?? -1,
|
||||||
url: req.body.url ?? '',
|
url: req.body.url ?? '',
|
||||||
wholeDay: req.body.wholeDay ?? false
|
wholeDay: req.body.wholeDay ?? false
|
||||||
};
|
};
|
||||||
|
@ -165,10 +175,14 @@ eventsRouter.post('/', async (req: Request, res: Response) => {
|
||||||
eventsRouter.put('/:eventId', async (req: Request, res: Response) => {
|
eventsRouter.put('/:eventId', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
// Get params
|
// 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)) {
|
let user = await UserService.checkSession(sessionId, sessionKey, ip);
|
||||||
res.status(403).send({'message': 'Insufficient privileges.'});
|
|
||||||
|
if (!user.isActive) {
|
||||||
|
res.status(403).send({'message': 'You do not have access to the specified calendar.'});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,8 +191,7 @@ eventsRouter.put('/:eventId', async (req: Request, res: Response) => {
|
||||||
req.body.calendarId === undefined ||
|
req.body.calendarId === undefined ||
|
||||||
isNullOrBlank(req.body.name) ||
|
isNullOrBlank(req.body.name) ||
|
||||||
req.body.startDateTime === undefined ||
|
req.body.startDateTime === undefined ||
|
||||||
req.body.endDateTime === undefined ||
|
req.body.endDateTime === undefined
|
||||||
isNullOrBlank(req.body.createdBy)
|
|
||||||
) {
|
) {
|
||||||
res.status(400).send({'message': 'Required parameters missing'});
|
res.status(400).send({'message': 'Required parameters missing'});
|
||||||
return;
|
return;
|
||||||
|
@ -195,6 +208,7 @@ eventsRouter.put('/:eventId', async (req: Request, res: Response) => {
|
||||||
createdDate: new Date(),
|
createdDate: new Date(),
|
||||||
location: req.body.location ?? '',
|
location: req.body.location ?? '',
|
||||||
createdBy: req.body.createdBy ?? '',
|
createdBy: req.body.createdBy ?? '',
|
||||||
|
createdById: user.userId ?? -1,
|
||||||
url: req.body.url ?? '',
|
url: req.body.url ?? '',
|
||||||
wholeDay: req.body.wholeDay ?? false
|
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) => {
|
eventsRouter.delete('/:eventId', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
// Get params
|
// 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)) {
|
let user = await UserService.checkSession(sessionId, sessionKey, ip);
|
||||||
res.status(403).send({'message': 'Insufficient privileges.'});
|
|
||||||
|
if (!user.isActive) {
|
||||||
|
res.status(403).send({'message': 'You do not have access to the specified calendar.'});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,6 +265,7 @@ eventsRouter.delete('/:eventId', async (req: Request, res: Response) => {
|
||||||
createdDate: new Date(),
|
createdDate: new Date(),
|
||||||
location: '',
|
location: '',
|
||||||
createdBy: '',
|
createdBy: '',
|
||||||
|
createdById: -1,
|
||||||
url: '',
|
url: '',
|
||||||
wholeDay: false
|
wholeDay: false
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,7 +14,7 @@ export const getAllEvents = async (calendarId: number): Promise<Event[]> => {
|
||||||
let conn = await NachklangCalendarDB.getConnection();
|
let conn = await NachklangCalendarDB.getConnection();
|
||||||
let eventRows: Event[] = [];
|
let eventRows: Event[] = [];
|
||||||
try {
|
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);
|
const eventsRes = await conn.query(eventsQuery, calendarId);
|
||||||
|
|
||||||
for (let row of eventsRes) {
|
for (let row of eventsRes) {
|
||||||
|
@ -28,7 +28,8 @@ export const getAllEvents = async (calendarId: number): Promise<Event[]> => {
|
||||||
endDateTime: row.end_datetime,
|
endDateTime: row.end_datetime,
|
||||||
createdDate: row.created_date,
|
createdDate: row.created_date,
|
||||||
location: row.location,
|
location: row.location,
|
||||||
createdBy: row.created_by,
|
createdBy: row.full_name,
|
||||||
|
createdById: row.created_by_id,
|
||||||
url: row.url,
|
url: row.url,
|
||||||
wholeDay: row.whole_day
|
wholeDay: row.whole_day
|
||||||
});
|
});
|
||||||
|
@ -51,8 +52,8 @@ export const createEvent = async (event: Event): Promise<number> => {
|
||||||
let conn = await NachklangCalendarDB.getConnection();
|
let conn = await NachklangCalendarDB.getConnection();
|
||||||
try {
|
try {
|
||||||
let eventUUID = Guid.create().toString();
|
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 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.createdBy, event.url, event.wholeDay]);
|
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;
|
return eventsRes[0].event_id;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -72,8 +73,8 @@ export const createEvent = async (event: Event): Promise<number> => {
|
||||||
export const updateEvent = async (event: Event): Promise<number> => {
|
export const updateEvent = async (event: Event): Promise<number> => {
|
||||||
let conn = await NachklangCalendarDB.getConnection();
|
let conn = await NachklangCalendarDB.getConnection();
|
||||||
try {
|
try {
|
||||||
const eventsQuery = 'UPDATE events SET name = ?, description = ?, start_datetime = ?, end_datetime = ?, location = ?, created_by = ?, url = ?, whole_day = ? WHERE event_id = ?';
|
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.createdBy, event.url, event.wholeDay, event.eventId]);
|
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;
|
return eventsRes.affectedRows;
|
||||||
} catch (err) {
|
} 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 {
|
||||||
|
sessionId: number;
|
||||||
|
userId: number;
|
||||||
|
sessionKey: string;
|
||||||
|
sessionKeyHash: string;
|
||||||
|
createdDate?: Date;
|
||||||
|
validUntil?: Date;
|
||||||
|
lastIP: 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 {
|
||||||
|
userId: number;
|
||||||
|
fullName: string;
|
||||||
|
passwordHash: string;
|
||||||
|
email: string;
|
||||||
|
isActive: 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({
|
||||||
|
sessionId: session.sessionId,
|
||||||
|
sessionKey: session.sessionKey
|
||||||
|
});
|
||||||
|
} 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.sessionId) {
|
||||||
|
// Error logging in, probably wrong username / password
|
||||||
|
res.status(401).send(JSON.stringify({message: 'Wrong username and / or password', sessionId: -1, sessionKey: ''}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the session details back to the user
|
||||||
|
res.status(200).send({
|
||||||
|
sessionId: session.sessionId,
|
||||||
|
sessionKey: session.sessionKey
|
||||||
|
});
|
||||||
|
} 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.sessionId;
|
||||||
|
const session_key = req.body.sessionKey;
|
||||||
|
|
||||||
|
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.userId) {
|
||||||
|
// 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 {
|
||||||
|
sessionId: sessionId,
|
||||||
|
userId: userId,
|
||||||
|
sessionKey: sessionKey,
|
||||||
|
sessionKeyHash: 'HIDDEN',
|
||||||
|
lastIP: 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 {
|
||||||
|
sessionId: sessionId,
|
||||||
|
userId: userId,
|
||||||
|
sessionKey: sessionKey,
|
||||||
|
sessionKeyHash: 'HIDDEN',
|
||||||
|
lastIP: 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 {
|
||||||
|
userId: userId,
|
||||||
|
email: email,
|
||||||
|
passwordHash: 'HIDDEN',
|
||||||
|
fullName: fullName,
|
||||||
|
isActive: is_active
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
} finally {
|
||||||
|
// Return connection
|
||||||
|
await conn.end();
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user