diff --git a/src/abstracts/index.ts b/src/abstracts/index.ts index 6fe0eec..1fbf613 100644 --- a/src/abstracts/index.ts +++ b/src/abstracts/index.ts @@ -1,6 +1,5 @@ export * from './controller'; export * from './shield'; -export * from './session_provider'; export * from './guard'; export * from './view_engine'; export * from './wall'; diff --git a/src/abstracts/session_provider.ts b/src/abstracts/session_provider.ts deleted file mode 100644 index 37776b9..0000000 --- a/src/abstracts/session_provider.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { CookieManager } from "../models"; -import * as getUniqId from "uniqid"; -import { FORT_GLOBAL } from "../constants/fort_global"; - -export abstract class SessionProvider { - - sessionId: string; - protected cookie: CookieManager; - - abstract get(key: string): Promise; - abstract isExist(key: string): Promise; - abstract getAll(): Promise<{ [key: string]: any }>; - abstract set(key: string, val: any): Promise; - abstract setMany(values: { [key: string]: any }): Promise; - abstract remove(key: string): Promise; - - abstract clear(): Promise; - - protected createSession(sessionId?) { - const now = new Date(); - this.sessionId = sessionId != null ? sessionId : getUniqId(); - this.cookie.addCookie({ - name: FORT_GLOBAL.appSessionIdentifier, - value: this.sessionId, - httpOnly: true, - path: "/", - expires: new Date(now.setMinutes(now.getMinutes() + FORT_GLOBAL.sessionTimeOut)), - maxAge: FORT_GLOBAL.sessionTimeOut * 60 - }); - } - - protected destroySession() { - const cookie = this.cookie.getCookie(FORT_GLOBAL.appSessionIdentifier); - cookie.httpOnly = true; - cookie.path = "/"; - this.cookie.removeCookie(cookie); - } -} - diff --git a/src/constants/fort_global.ts b/src/constants/fort_global.ts index ff80f26..b24f016 100644 --- a/src/constants/fort_global.ts +++ b/src/constants/fort_global.ts @@ -1,13 +1,13 @@ import { ErrorHandler, Logger } from "../models"; -import { ViewEngine, XmlParser, ComponentOption, Shield } from "../abstracts"; -import { EtagOption, FolderMap } from "../types"; -import { GenericGuard, GenericSessionProvider, GenericShield, GenericWall, GenericXmlParser } from "../generics"; -import { MustacheViewEngine, MemorySessionProvider, DtoValidator } from "../extra"; +import { ViewEngine, XmlParser, ComponentOption } from "../abstracts"; +import { EtagOption, FolderMap, TSessionStore } from "../types"; +import { GenericGuard, GenericShield, GenericWall, GenericXmlParser } from "../generics"; +import { MustacheViewEngine, DtoValidator } from "../extra"; import { APP_NAME, CURRENT_PATH } from "./index"; import * as path from "path"; import { ETAG_TYPE } from "../enums"; import { IDtoValidator } from "../interfaces"; -import { CookieEvaluatorWall, PostDataEvaluatorGuard } from "../providers"; +import { CookieEvaluatorWall, MemorySessionStore, PostDataEvaluatorGuard } from "../providers"; const isDevelopment = process.env.NODE_ENV === 'development'; const isProduction = process.env.NODE_ENV === "production"; @@ -17,7 +17,7 @@ export class FortGlobal { viewPath; shouldParseCookie = true; shouldParseBody = true; - sessionProvider: typeof GenericSessionProvider; + sessionStore: TSessionStore; sessionTimeOut = 60; viewEngine: ViewEngine; walls: Array = []; @@ -58,9 +58,7 @@ export class FortGlobal { this.logger = this.logger || new Logger(); } - if (this.sessionProvider == null) { - this.sessionProvider = MemorySessionProvider as any; - } + this.sessionStore = this.sessionStore || MemorySessionStore; if (this.xmlParser == null) { this.xmlParser = GenericXmlParser; diff --git a/src/extra/index.ts b/src/extra/index.ts index 8eae3dc..9a09141 100644 --- a/src/extra/index.ts +++ b/src/extra/index.ts @@ -1,5 +1,2 @@ export * from './mustache_view_engine'; -export * from "./memory_session_provider"; -export * from "./dto_validator"; -// export * from "./expect_body_guard"; -// export * from "./expect_query_shield"; \ No newline at end of file +export * from "./dto_validator"; \ No newline at end of file diff --git a/src/generics/generic_session_provider.ts b/src/generics/generic_session_provider.ts deleted file mode 100644 index 23733ac..0000000 --- a/src/generics/generic_session_provider.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { SessionProvider } from "../abstracts"; -import { SessionValue } from "../types"; -import { CookieManager } from "../models"; - - -export class GenericSessionProvider extends SessionProvider { - sessionId: string; - - cookie: CookieManager; - get() { - return null; - } - - getAll() { - return null; - } - - set(key, value) { - return null; - } - - isExist(key) { - return null; - } - - remove(key) { - return null; - } - - setMany(values: SessionValue[]): Promise { - return null; - } - - // eslint-disable-next-line - clear() { - return null; - } -} \ No newline at end of file diff --git a/src/generics/index.ts b/src/generics/index.ts index 359f374..6fbcc03 100644 --- a/src/generics/index.ts +++ b/src/generics/index.ts @@ -1,6 +1,5 @@ export * from './generic_guard'; export * from './generic_shield'; -export * from './generic_session_provider'; export * from "./generic_wall"; export * from "./generic_controller"; export * from './generic_xml_parser'; \ No newline at end of file diff --git a/src/interfaces/component_prop.ts b/src/interfaces/component_prop.ts index 9f45a44..dbeaf81 100644 --- a/src/interfaces/component_prop.ts +++ b/src/interfaces/component_prop.ts @@ -1,14 +1,14 @@ import * as http from "http"; -import { SessionProvider } from "../abstracts"; import { CookieManager, FileManager } from "../models"; import { FortGlobal } from "../constants"; +import { SessionManager } from "../utils"; export interface IComponentProp { request: http.IncomingMessage; response: http.ServerResponse; query: { [key: string]: any }; body?: { [key: string]: any }; - session: SessionProvider; + session: SessionManager; cookie: CookieManager; param?: { [key: string]: string }; data: { [key: string]: any }; diff --git a/src/interfaces/controller.ts b/src/interfaces/controller.ts index 5738e33..a8962c9 100644 --- a/src/interfaces/controller.ts +++ b/src/interfaces/controller.ts @@ -1,13 +1,13 @@ import { HttpRequest, HttpResponse } from "../types"; import { CookieManager, FileManager } from "../models"; -import { SessionProvider } from "../abstracts"; +import { SessionManager } from "../utils"; export interface IController { request: HttpRequest; response: HttpResponse; query: { [key: string]: string }; body?: { [key: string]: any }; - session: SessionProvider; + session: SessionManager; cookie: CookieManager; param?: { [key: string]: string }; data: { [key: string]: any }; diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts index 1379301..0de302c 100644 --- a/src/interfaces/index.ts +++ b/src/interfaces/index.ts @@ -6,4 +6,5 @@ export * from './worker_info'; export * from './component_prop'; export * from './validator'; export * from './http_result'; +export * from './session_store'; diff --git a/src/interfaces/session_store.ts b/src/interfaces/session_store.ts new file mode 100644 index 0000000..2e49c6e --- /dev/null +++ b/src/interfaces/session_store.ts @@ -0,0 +1,11 @@ +export interface ISessonStore { + sessionId: string; + get(key: string): Promise; + isAnyExist(): Promise; + isExist(key: string): Promise; + getAll(): Promise<{ [key: string]: any }>; + set(key: string, val: any): Promise; + setMany(values: { [key: string]: any }): Promise; + remove(key: string): Promise; + clear(): Promise; +} \ No newline at end of file diff --git a/src/models/fort.ts b/src/models/fort.ts index c042034..b4a87da 100644 --- a/src/models/fort.ts +++ b/src/models/fort.ts @@ -1,12 +1,12 @@ -import { ParentRoute, EtagOption, FolderMap } from "../types"; -import { Wall, ViewEngine, SessionProvider, XmlParser, ResultMapper, Shield, Guard } from "../abstracts"; +import { ParentRoute, EtagOption, FolderMap, TSessionStore } from "../types"; +import { Wall, ViewEngine, XmlParser, ResultMapper, Shield, Guard } from "../abstracts"; import { RouteHandler, RequestHandler } from "../handlers"; import { FORT_GLOBAL } from "../constants/fort_global"; import { ErrorHandler } from "."; import * as http from "http"; import { ERROR_TYPE } from "../enums"; import { LogHelper, promise, removeLastSlash, removeFirstSlash, setResultMapper } from "../helpers"; -import { GenericSessionProvider, GenericController } from "../generics"; +import { GenericController } from "../generics"; import { isArray } from "../utils"; import { Logger } from "./logger"; import { ComponentOption } from "../abstracts/component_option"; @@ -99,13 +99,13 @@ export class Fort { } /** - * sessionProvider class, default - MemorySessionProvider + * sessionStore class, default - MemorySessionStore * * @static * @memberof Fort */ - static set sessionProvider(value: typeof SessionProvider) { - FORT_GLOBAL.sessionProvider = value as typeof GenericSessionProvider; + static set sessionStore(value: TSessionStore) { + FORT_GLOBAL.sessionStore = value; } static set resultMapper(value: typeof ResultMapper) { diff --git a/src/providers/cookie_wall.ts b/src/providers/cookie_wall.ts index 06670f9..26e2178 100644 --- a/src/providers/cookie_wall.ts +++ b/src/providers/cookie_wall.ts @@ -3,6 +3,7 @@ import { COOKIE, FORT_GLOBAL } from "../constants"; import { parseCookie } from "../helpers"; import { IHttpResult } from "../interfaces"; import { CookieManager } from "../models"; +import { SessionManager } from "../utils"; export class CookieEvaluatorWall extends Wall { parseCookieFromRequest() { @@ -14,11 +15,10 @@ export class CookieEvaluatorWall extends Wall { const request = this.request; const rawCookie = (request.headers[COOKIE] || request.headers["cookie"]) as string; const parsedCookies = parseCookie(rawCookie); - const session = new FORT_GLOBAL.sessionProvider(); - session.cookie = new CookieManager(parsedCookies); - session.sessionId = parsedCookies[FORT_GLOBAL.appSessionIdentifier]; + const cookie = new CookieManager(parsedCookies); + const session = new SessionManager(cookie, FORT_GLOBAL.sessionStore); componentProps.session = session; - componentProps.cookie = session.cookie; + componentProps.cookie = cookie; } async onIncoming(): Promise { diff --git a/src/providers/index.ts b/src/providers/index.ts index 884749c..ceb0b1e 100644 --- a/src/providers/index.ts +++ b/src/providers/index.ts @@ -1,2 +1,3 @@ export * from "./cookie_wall"; -export * from "./post_data_evaluator_guard"; \ No newline at end of file +export * from "./post_data_evaluator_guard"; +export * from "./memory_session_store"; \ No newline at end of file diff --git a/src/extra/memory_session_provider.ts b/src/providers/memory_session_store.ts similarity index 54% rename from src/extra/memory_session_provider.ts rename to src/providers/memory_session_store.ts index 915d3cc..e0b7841 100644 --- a/src/extra/memory_session_provider.ts +++ b/src/providers/memory_session_store.ts @@ -1,33 +1,45 @@ -import { SessionProvider } from "../abstracts/session_provider"; -import { promiseResolve } from "../utils"; +import { ISessonStore } from "../interfaces"; const sessionValues: Map = new Map(); -export class MemorySessionProvider extends SessionProvider { +export class MemorySessionStore implements ISessonStore { + sessionId: string; + + constructor(sessionId: string) { + this.sessionId = sessionId; + } private getSessionValue_() { return sessionValues.get(this.sessionId); } - get(key: string) { - const savedValue = this.getSessionValue_() - return promiseResolve(savedValue != null ? savedValue[key] : null); + async isAnyExist() { + return this.getSessionValue_() != null; } - isExist(key: string) { - const savedValue = this.getSessionValue_() - return promiseResolve(savedValue == null ? false : savedValue[key] != null); + async get(key: string): Promise { + const savedValue = this.getSessionValue_(); + return savedValue != null ? savedValue[key] : null } - getAll() { + async getAll(): Promise<{ [key: string]: any; }> { const savedValue = this.getSessionValue_(); - return promiseResolve(savedValue || {}); + return savedValue || {}; + } + + async isExist(key: string): Promise { + const savedValue = this.getSessionValue_() + return savedValue == null ? false : savedValue[key] != null; } - set(key: string, val: any) { + async clear(): Promise { + // remove session values + sessionValues.delete(this.sessionId); + } + + async set(key: string, val: any) { const savedValue = this.getSessionValue_(); if (savedValue == null) { - this.createSession(); sessionValues.set(this.sessionId, { [key]: val }); @@ -35,7 +47,6 @@ export class MemorySessionProvider extends SessionProvider { else { savedValue[key] = val; } - return promiseResolve(null); } setMany(values: { [key: string]: any }) { @@ -46,19 +57,10 @@ export class MemorySessionProvider extends SessionProvider { ); } - remove(key: string) { + async remove(key: string) { const savedValue = this.getSessionValue_(); if (savedValue != null) { savedValue[key] = null; } - return promiseResolve(null); - } - - clear() { - // remove session values - sessionValues.delete(this.sessionId); - // expire cookie in browser - this.destroySession(); - return promiseResolve(null); } } \ No newline at end of file diff --git a/src/test_helpers/init_controller.ts b/src/test_helpers/init_controller.ts index dc93c90..0294b58 100644 --- a/src/test_helpers/init_controller.ts +++ b/src/test_helpers/init_controller.ts @@ -5,6 +5,7 @@ import { ControllerTestData } from "../types"; import { HttpResponseStub } from "./http_response_stub"; import { HttpRequestStub } from "./http_request_stub"; import { Controller } from "../abstracts"; +import { SessionManager } from "../utils"; @@ -12,10 +13,11 @@ export const initController = (controllerInstance: Controller, data?: Controller data = data || {}; const parsedCookies = data.cookieValue || {}; const headers = (data.request && data.request.headers) || {}; - const session = new FORT_GLOBAL.sessionProvider(); const cookie = new CookieManager(parsedCookies); - session.cookie = cookie; - session.sessionId = parsedCookies[FORT_GLOBAL.appSessionIdentifier]; + const session = new SessionManager( + cookie, + FORT_GLOBAL.sessionStore + ); controllerInstance['componentProp_'] = { request: new HttpRequestStub(headers) as any, response: new HttpResponseStub(headers) as any, diff --git a/src/types/index.ts b/src/types/index.ts index 371569d..1932d2c 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,4 +1,4 @@ -import { IHttpResult } from '../interfaces'; +import { IHttpResult, ISessonStore } from '../interfaces'; export * from './view_engine_data'; export * from './http_request'; @@ -16,3 +16,5 @@ export * from './view_read_option'; export * from './http_format_result'; export type ErrorResultMapper = (error: any) => IHttpResult; +type Class = new (...args: Args) => I; +export type TSessionStore = Class; \ No newline at end of file diff --git a/src/utils/index.ts b/src/utils/index.ts index 1d5234e..4f5d876 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -2,4 +2,5 @@ export * from './is_null_or_empty'; export * from './is_null'; export * from './is_array'; export * from './promise_resolve'; -export * from './compare_string'; \ No newline at end of file +export * from './compare_string'; +export * from './session_manager'; \ No newline at end of file diff --git a/src/utils/session_manager.ts b/src/utils/session_manager.ts new file mode 100644 index 0000000..35dde00 --- /dev/null +++ b/src/utils/session_manager.ts @@ -0,0 +1,74 @@ +import { FORT_GLOBAL } from "../constants"; +import { ISessonStore } from "../interfaces"; +import { CookieManager } from "../models"; +import * as getUniqId from "uniqid"; +import { TSessionStore } from "../types"; + +export class SessionManager { + + sessionId: string; + protected cookie: CookieManager; + sessionStore: ISessonStore; + + constructor(cookie: CookieManager, sessionStore: TSessionStore) { + this.sessionId = cookie.cookieCollection[FORT_GLOBAL.appSessionIdentifier]; + this.sessionStore = new sessionStore(this.sessionId); + this.cookie = cookie; + } + + protected createSession(sessionId?) { + const now = new Date(); + this.sessionId = sessionId != null ? sessionId : getUniqId(); + this.cookie.addCookie({ + name: FORT_GLOBAL.appSessionIdentifier, + value: this.sessionId, + httpOnly: true, + path: "/", + expires: new Date(now.setMinutes(now.getMinutes() + FORT_GLOBAL.sessionTimeOut)), + maxAge: FORT_GLOBAL.sessionTimeOut * 60 + }); + } + + protected destroySession() { + const cookie = this.cookie.getCookie(FORT_GLOBAL.appSessionIdentifier); + cookie.httpOnly = true; + cookie.path = "/"; + this.cookie.removeCookie(cookie); + } + + get(key: string) { + return this.sessionStore.get(key); + } + + isExist(key: string) { + return this.sessionStore.isExist(key); + } + + getAll() { + return this.sessionStore.getAll(); + } + + async set(key: string, val: any) { + const savedValue = await this.sessionStore.isAnyExist(); + if (savedValue === false) { + this.createSession(); + this.sessionStore.sessionId = this.sessionId; + } + await this.sessionStore.set(key, val); + } + + setMany(values: { [key: string]: any }) { + return this.sessionStore.setMany(values); + } + + remove(key: string) { + return this.sessionStore.remove(key); + } + + async clear() { + // remove session values + await this.sessionStore.clear(); + // expire cookie in browser + this.destroySession(); + } +} \ No newline at end of file diff --git a/tests/general/controllers/home_controller.ts b/tests/general/controllers/home_controller.ts index a7f5878..231b0dd 100644 --- a/tests/general/controllers/home_controller.ts +++ b/tests/general/controllers/home_controller.ts @@ -29,8 +29,8 @@ export class HomeController extends Controller { const userService = new UserService(); const user = userService.getUserByEmail(emailId); if (user != null && (this.option as MyComponentOption).timingSafeEqual(user.password, pwd)) { - this.session.set('userId', user.id); - this.session.set('emailId', emailId); + await this.session.set('userId', user.id); + await this.session.set('emailId', emailId); return textResult(`Authenticated`); } else {