This commit is contained in:
Tutur33
2023-11-24 22:35:41 +01:00
parent 3c0b507a93
commit 7644b2a0f7
45165 changed files with 4803356 additions and 3 deletions
+193
View File
@@ -0,0 +1,193 @@
/// <reference path="../../adonis-typings/session.d.ts" />
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
import { SessionConfig, SessionContract, AllowedSessionValues, SessionDriverContract } from '@ioc:Adonis/Addons/Session';
import { Store } from '../Store';
/**
* Session class exposes the API to read/write values to the session for
* a given request.
*/
export declare class Session implements SessionContract {
private ctx;
private config;
private driver;
/**
* Set to true inside the `initiate` method
*/
initiated: boolean;
/**
* A boolean to know if it's a fresh session or not. Fresh
* sessions are those, whose session id is not present
* in cookie
*/
fresh: boolean;
/**
* A boolean to know if store is initiated in readonly mode
* or not. This is done during Websocket requests
*/
readonly: boolean;
/**
* Session id for the given request. A new session id is only
* generated when the cookie for the session id is missing
*/
sessionId: string;
/**
* A copy of previously set flash messages
*/
flashMessages: Store;
/**
* Session id for the current request. It will be different
* from the "this.sessionId" when regenerate is called.
*/
private currentSessionId;
/**
* A instance of store with values read from the driver. The store
* in initiated inside the [[initiate]] method
*/
private store;
/**
* Whether or not to re-generate the session id before comitting
* session values.
*/
private regeneratedSessionId;
/**
* A copy of flash messages. The `input` messages
* are overwritten when any of the input related
* methods are used.
*
* The `others` object is expanded with each call.
*/
responseFlashMessages: Store;
/**
* Session key for setting flash messages
*/
private flashMessagesKey;
constructor(ctx: HttpContextContract, config: SessionConfig, driver: SessionDriverContract);
/**
* Returns a merged copy of flash messages or null
* when nothing is set
*/
private setFlashMessages;
/**
* Returns the existing session id or creates one.
*/
private getSessionId;
/**
* Ensures the session store is initialized
*/
private ensureIsReady;
/**
* Raises exception when session store is in readonly mode
*/
private ensureIsMutable;
/**
* Touches the session cookie
*/
private touchSessionCookie;
/**
* Commits the session value to the store
*/
private commitValuesToStore;
/**
* Touches the driver to make sure the session values doesn't expire
*/
private touchDriver;
/**
* Reading flash messages from the last HTTP request and
* updating the flash messages bag
*/
private readLastRequestFlashMessage;
/**
* Share flash messages & read only session's functions with views
* (only when view property exists)
*/
private shareLocalsWithView;
/**
* Initiating the session by reading it's value from the
* driver and feeding it to a store.
*
* Multiple calls to `initiate` results in a noop.
*/
initiate(readonly: boolean): Promise<void>;
/**
* Re-generates the session id. This can is used to avoid
* session fixation attacks.
*/
regenerate(): void;
/**
* Set/update session value
*/
put(key: string, value: AllowedSessionValues): void;
/**
* Find if the value exists in the session
*/
has(key: string): boolean;
/**
* Get value from the session. The default value is returned
* when actual value is `undefined`
*/
get(key: string, defaultValue?: any): any;
/**
* Returns everything from the session
*/
all(): any;
/**
* Remove value for a given key from the session
*/
forget(key: string): void;
/**
* The method is equivalent to calling `session.get` followed
* by `session.forget`
*/
pull(key: string, defaultValue?: any): any;
/**
* Increment value for a number inside the session store. The
* method raises an error when underlying value is not
* a number
*/
increment(key: string, steps?: number): void;
/**
* Decrement value for a number inside the session store. The
* method raises an error when underlying value is not
* a number
*/
decrement(key: string, steps?: number): void;
/**
* Remove everything from the session
*/
clear(): void;
/**
* Add a new flash message
*/
flash(key: string | {
[key: string]: AllowedSessionValues;
}, value?: AllowedSessionValues): void;
/**
* Flash all form values
*/
flashAll(): void;
/**
* Flash all form values except mentioned keys
*/
flashExcept(keys: string[]): void;
/**
* Flash only defined keys from the form values
*/
flashOnly(keys: string[]): void;
/**
* Reflash existing flash messages
*/
reflash(): void;
/**
* Reflash selected keys from the existing flash messages
*/
reflashOnly(keys: string[]): void;
/**
* Omit selected keys from the existing flash messages
* and flash the rest of values
*/
reflashExcept(keys: string[]): void;
/**
* Writes value to the underlying session driver.
*/
commit(): Promise<void>;
}
+352
View File
@@ -0,0 +1,352 @@
"use strict";
/*
* @adonisjs/session
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Session = void 0;
/// <reference path="../../adonis-typings/session.ts" />
const utils_1 = require("@poppinss/utils");
const helpers_1 = require("@poppinss/utils/build/helpers");
const Store_1 = require("../Store");
/**
* Session class exposes the API to read/write values to the session for
* a given request.
*/
class Session {
constructor(ctx, config, driver) {
this.ctx = ctx;
this.config = config;
this.driver = driver;
/**
* Set to true inside the `initiate` method
*/
this.initiated = false;
/**
* A boolean to know if it's a fresh session or not. Fresh
* sessions are those, whose session id is not present
* in cookie
*/
this.fresh = false;
/**
* A boolean to know if store is initiated in readonly mode
* or not. This is done during Websocket requests
*/
this.readonly = false;
/**
* Session id for the given request. A new session id is only
* generated when the cookie for the session id is missing
*/
this.sessionId = this.getSessionId();
/**
* A copy of previously set flash messages
*/
this.flashMessages = new Store_1.Store({});
/**
* Session id for the current request. It will be different
* from the "this.sessionId" when regenerate is called.
*/
this.currentSessionId = this.sessionId;
/**
* Whether or not to re-generate the session id before comitting
* session values.
*/
this.regeneratedSessionId = false;
/**
* A copy of flash messages. The `input` messages
* are overwritten when any of the input related
* methods are used.
*
* The `others` object is expanded with each call.
*/
this.responseFlashMessages = new Store_1.Store({});
/**
* Session key for setting flash messages
*/
this.flashMessagesKey = '__flash__';
}
/**
* Returns a merged copy of flash messages or null
* when nothing is set
*/
setFlashMessages() {
if (this.responseFlashMessages.isEmpty) {
return;
}
const { input, ...others } = this.responseFlashMessages.all();
this.put(this.flashMessagesKey, { ...input, ...others });
}
/**
* Returns the existing session id or creates one.
*/
getSessionId() {
const sessionId = this.ctx.request.cookie(this.config.cookieName);
if (sessionId) {
this.ctx.logger.trace('existing session found');
return sessionId;
}
this.fresh = true;
this.ctx.logger.trace('generating new session id');
return (0, helpers_1.cuid)();
}
/**
* Ensures the session store is initialized
*/
ensureIsReady() {
if (!this.initiated) {
throw new utils_1.Exception('Session store is not initiated yet. Make sure you are using the session hook', 500, 'E_RUNTIME_EXCEPTION');
}
}
/**
* Raises exception when session store is in readonly mode
*/
ensureIsMutable() {
if (this.readonly) {
throw new utils_1.Exception('Session store is in readonly mode and cannot be mutated', 500, 'E_RUNTIME_EXCEPTION');
}
}
/**
* Touches the session cookie
*/
touchSessionCookie() {
this.ctx.logger.trace('touching session cookie');
this.ctx.response.cookie(this.config.cookieName, this.sessionId, this.config.cookie);
}
/**
* Commits the session value to the store
*/
async commitValuesToStore() {
this.ctx.logger.trace('persist session store with driver');
await this.driver.write(this.sessionId, this.store.toJSON());
}
/**
* Touches the driver to make sure the session values doesn't expire
*/
async touchDriver() {
this.ctx.logger.trace('touch driver for liveliness');
await this.driver.touch(this.sessionId);
}
/**
* Reading flash messages from the last HTTP request and
* updating the flash messages bag
*/
readLastRequestFlashMessage() {
if (this.readonly) {
return;
}
this.flashMessages.update(this.pull(this.flashMessagesKey, null));
}
/**
* Share flash messages & read only session's functions with views
* (only when view property exists)
*/
shareLocalsWithView() {
if (!this.ctx['view'] || typeof this.ctx['view'].share !== 'function') {
return;
}
this.ctx['view'].share({
flashMessages: this.flashMessages,
session: {
get: this.get.bind(this),
has: this.has.bind(this),
all: this.all.bind(this),
},
});
}
/**
* Initiating the session by reading it's value from the
* driver and feeding it to a store.
*
* Multiple calls to `initiate` results in a noop.
*/
async initiate(readonly) {
if (this.initiated) {
return;
}
this.readonly = readonly;
/**
* Profiling the driver read method
*/
await this.ctx.profiler.profileAsync('session:initiate', { driver: this.config.driver }, async () => {
const contents = await this.driver.read(this.sessionId);
this.store = new Store_1.Store(contents);
});
this.initiated = true;
this.readLastRequestFlashMessage();
this.shareLocalsWithView();
}
/**
* Re-generates the session id. This can is used to avoid
* session fixation attacks.
*/
regenerate() {
this.ctx.logger.trace('explicitly re-generating session id');
this.sessionId = (0, helpers_1.cuid)();
this.regeneratedSessionId = true;
}
/**
* Set/update session value
*/
put(key, value) {
this.ensureIsReady();
this.ensureIsMutable();
this.store.set(key, value);
}
/**
* Find if the value exists in the session
*/
has(key) {
this.ensureIsReady();
return this.store.has(key);
}
/**
* Get value from the session. The default value is returned
* when actual value is `undefined`
*/
get(key, defaultValue) {
this.ensureIsReady();
return this.store.get(key, defaultValue);
}
/**
* Returns everything from the session
*/
all() {
this.ensureIsReady();
return this.store.all();
}
/**
* Remove value for a given key from the session
*/
forget(key) {
this.ensureIsReady();
this.ensureIsMutable();
this.store.unset(key);
}
/**
* The method is equivalent to calling `session.get` followed
* by `session.forget`
*/
pull(key, defaultValue) {
this.ensureIsReady();
this.ensureIsMutable();
return this.store.pull(key, defaultValue);
}
/**
* Increment value for a number inside the session store. The
* method raises an error when underlying value is not
* a number
*/
increment(key, steps = 1) {
this.ensureIsReady();
this.ensureIsMutable();
this.store.increment(key, steps);
}
/**
* Decrement value for a number inside the session store. The
* method raises an error when underlying value is not
* a number
*/
decrement(key, steps = 1) {
this.ensureIsReady();
this.ensureIsMutable();
this.store.decrement(key, steps);
}
/**
* Remove everything from the session
*/
clear() {
this.ensureIsReady();
this.ensureIsMutable();
this.store.clear();
}
/**
* Add a new flash message
*/
flash(key, value) {
this.ensureIsReady();
this.ensureIsMutable();
/**
* Update value
*/
if (typeof key === 'string') {
if (value) {
this.responseFlashMessages.set(key, value);
}
}
else {
this.responseFlashMessages.merge(key);
}
}
/**
* Flash all form values
*/
flashAll() {
this.ensureIsReady();
this.ensureIsMutable();
this.responseFlashMessages.set('input', this.ctx.request.original());
}
/**
* Flash all form values except mentioned keys
*/
flashExcept(keys) {
this.ensureIsReady();
this.ensureIsMutable();
this.responseFlashMessages.set('input', utils_1.lodash.omit(this.ctx.request.original(), keys));
}
/**
* Flash only defined keys from the form values
*/
flashOnly(keys) {
this.ensureIsReady();
this.ensureIsMutable();
this.responseFlashMessages.set('input', utils_1.lodash.pick(this.ctx.request.original(), keys));
}
/**
* Reflash existing flash messages
*/
reflash() {
this.flash(this.flashMessages.all());
}
/**
* Reflash selected keys from the existing flash messages
*/
reflashOnly(keys) {
this.flash(utils_1.lodash.pick(this.flashMessages.all(), keys));
}
/**
* Omit selected keys from the existing flash messages
* and flash the rest of values
*/
reflashExcept(keys) {
this.flash(utils_1.lodash.omit(this.flashMessages.all(), keys));
}
/**
* Writes value to the underlying session driver.
*/
async commit() {
await this.ctx.profiler.profileAsync('session:commit', { driver: this.config.driver }, async () => {
if (!this.initiated) {
this.touchSessionCookie();
await this.touchDriver();
return;
}
/**
* Cleanup old session and re-generate new session
*/
if (this.regeneratedSessionId) {
await this.driver.destroy(this.currentSessionId);
}
/**
* Touch the session cookie to keep it alive.
*/
this.touchSessionCookie();
this.setFlashMessages();
await this.commitValuesToStore();
});
}
}
exports.Session = Session;