summaryrefslogtreecommitdiff
path: root/src/routers/vault/types/SessionStore.js
blob: 26575518bef031598af267cc4eade61ee436664b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
"use strict";
const Session = require("./Session.js");

/**
 * we store the timeout directly in Session instances
 * @type {Symbol("session timeout")}
 */
const timeout_symbol = Symbol("session timeout");

/**
 * holds the Sessions and re-hydrates them when accessed
 */
class SessionStore {
    /**
     * a Map of all Session instances
     * @type {Map<string, Session>}
    */
    sessions = new Map();
    /** lifetime of an unauthenticated Session, in minutes */
    base_lifetime = 30;
    /** lifetime of a properly authenticated Session, in minutes */
    authed_lifetime = 60 * 6; // 6 hours

    /**
     * creates a new SessionStore
     * @param {number} base - the base Session lifetime, in minutes
     * @param {number} authed - the lifetime of an authenticated Session
     */
    constructor (base = 0, authed = 0) {
        this.base_lifetime = base || this.base_lifetime;
        this.authed_lifetime = authed || this.authed_lifetime;
    }

    /**
     * re-hydrates a Session by resetting the expiration
     * @param {string} key - the Session key
     * @param {Session} sess - the Session to hydrate
     * @returns {Session} the same Session
     */
    hydrate (key, sess) {
        /** the new expiration, in minutes */
        let minutes = this.base_lifetime;

        if (Reflect.has(sess, timeout_symbol)) {
            /** clear any existing timeout */
            clearTimeout(sess[timeout_symbol]);
        }

        if (sess.authenticated === true) {
            /** Session is properly authenticated: set lifetime accordingly */
            minutes = this.authed_lifetime;
        }

        /** the new expiry Date */
        const expires = new Date();
        expires.setUTCMinutes(expires.getUTCMinutes() + minutes); // update it
        sess.expires = expires; // swap the old for the new expiry
        sess[timeout_symbol] = setTimeout(() => this.delete(key), minutes * 60000);
        return sess;
    }

    /**
     * checks whether a Session with the given key exists
     * @param {string} key - the Session key
     */
    has (key) {
        return this.sessions.has(key);
    }

    /**
     * returns a Session with the matching key
     * @param {string} key - the Session key
     * @returns {Session} the found Session
     */
    get (key) {
        /** lookup the session by key */
        const sess = this.sessions.get(key);

        if (sess) {
            /** the Session, re-hydrated */
            return this.hydrate(key, sess);
        }

        /** session not found */
        return null;
    }

    /**
     * adds a Session to the store
     * @param {string} key - the Session key
     * @param {Session} sess - the Session
     */
    set (key, sess) {
        this.sessions.set(key, this.hydrate(key, sess));
    }

    /**
     * removes a Session with the matching key from the store
     *
     * NOTE: this does not actually delete the Session instance
     * @param {string} key - the Session key
     */
    delete (key) {
        /** lookup the session by key */
        const sess = this.sessions.get(key);

        if (sess) {
            if (Reflect.has(sess, timeout_symbol)) {
                /** clear any existing timeout */
                clearTimeout(sess[timeout_symbol]);
            }

            return this.sessions.delete(key)
        }

        /** session not found */
        return false;
    }

    /**
     * iterator for use in for-of
     * @returns {Iterator<[string, Session]>} the Map iterator of the SessionStore instance
     */
    [Symbol.iterator] () {
        return this.sessions[Symbol.iterator]();
    }
}

module.exports = SessionStore;