connect-memcached 1.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Readme.md +50 -15
- package/docker-compose.yml +104 -0
- package/flake.lock +61 -0
- package/flake.nix +39 -0
- package/index.js +1 -2
- package/lib/connect-memcached.js +30 -88
- package/package.json +18 -3
- package/test/services/memcached_basic.js +40 -0
- package/test/services/memcached_crypt.js +41 -0
- package/test/services/memcached_preexisting_crypt_connection.js +44 -0
- package/test/test.js +63 -0
- package/test-all.sh +39 -0
- package/.npmignore +0 -2
- package/tests/test.js +0 -34
- package/tests/test_encrypt.js +0 -35
package/Readme.md
CHANGED
|
@@ -4,10 +4,16 @@ Memcached session store, using [node-memcached](http://github.com/3rd-Eden/node-
|
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
npm:
|
|
8
8
|
|
|
9
|
-
```
|
|
10
|
-
|
|
9
|
+
```shell
|
|
10
|
+
npm install connect-memcached express-session
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
yarn:
|
|
14
|
+
|
|
15
|
+
```shell
|
|
16
|
+
yarn add connect-memcached express-session
|
|
11
17
|
```
|
|
12
18
|
|
|
13
19
|
## Example
|
|
@@ -15,12 +21,9 @@ $ npm install connect-memcached
|
|
|
15
21
|
```javascript
|
|
16
22
|
var express = require("express"),
|
|
17
23
|
session = require("express-session"),
|
|
18
|
-
cookieParser = require("cookie-parser"),
|
|
19
|
-
http = require("http"),
|
|
20
24
|
app = express(),
|
|
21
25
|
MemcachedStore = require("connect-memcached")(session);
|
|
22
26
|
|
|
23
|
-
app.use(cookieParser());
|
|
24
27
|
app.use(
|
|
25
28
|
session({
|
|
26
29
|
secret: "CatOnKeyboard",
|
|
@@ -30,7 +33,7 @@ app.use(
|
|
|
30
33
|
saveUninitialized: false,
|
|
31
34
|
store: new MemcachedStore({
|
|
32
35
|
hosts: ["127.0.0.1:11211"],
|
|
33
|
-
secret: "
|
|
36
|
+
secret: "Xj8$kLp2@Qa9#Zt5!" // Optionally use transparent encryption for memcached session data (must meet complexity requirements)
|
|
34
37
|
})
|
|
35
38
|
})
|
|
36
39
|
);
|
|
@@ -44,21 +47,53 @@ app.get("/", function(req, res) {
|
|
|
44
47
|
res.send("Viewed <strong>" + req.session.views + "</strong> times.");
|
|
45
48
|
});
|
|
46
49
|
|
|
47
|
-
|
|
50
|
+
app.listen(9341, function() {
|
|
48
51
|
console.log("Listening on %d", this.address().port);
|
|
49
52
|
});
|
|
50
53
|
```
|
|
51
54
|
|
|
52
55
|
## Options
|
|
53
56
|
|
|
54
|
-
- `hosts` Memcached servers locations, can be string, array
|
|
55
|
-
- `prefix`
|
|
56
|
-
- `ttl`
|
|
57
|
-
- `secret`
|
|
58
|
-
- `algorithm`
|
|
59
|
-
-
|
|
57
|
+
- `hosts` (Optional) Memcached servers locations, can be string, array or hash. Default is `127.0.0.1:11211`.
|
|
58
|
+
- `prefix` (Optional) Prefix for each memcached key, in case you are sharing your memcached servers with something generating its own keys.
|
|
59
|
+
- `ttl` (Optional) Default TTL parameter for the session data (in seconds).
|
|
60
|
+
- `secret` (Optional) Secret used to encrypt/decrypt session contents. Setting it enables data encryption, which is handled by [kruptein](https://github.com/jas-/kruptein) module.
|
|
61
|
+
- `algorithm` (Optional) Cipher algorithm from `crypto.getCiphers()`. Default is `aes-256-gcm`.
|
|
62
|
+
- `hashing` (Optional) Hash algorithm from `crypto.getHashes()`. Default is `sha512`.
|
|
63
|
+
- ... Rest of given options will be passed directly to the [node-memcached](http://github.com/3rd-Eden/node-memcached) and [kruptein](https://github.com/jas-/kruptein) constructors, see their appropriate docs for extra configurability.
|
|
64
|
+
|
|
65
|
+
## Upgrading to v3.x.x
|
|
66
|
+
|
|
67
|
+
v3.0.0 introduces two breaking changes:
|
|
68
|
+
|
|
69
|
+
### 1. Node.js version requirement
|
|
70
|
+
|
|
71
|
+
**Breaking change:** v3.0.0 requires Node.js >= 14.0.0. Support for Node.js versions 4-12 has been dropped.
|
|
72
|
+
|
|
73
|
+
If you're running an older Node.js version, please upgrade to Node.js 14 or later before upgrading to v3.x.x.
|
|
74
|
+
|
|
75
|
+
### 2. Stronger secret requirements for encryption
|
|
76
|
+
|
|
77
|
+
**Breaking change:** The `kruptein` dependency has been updated to enforce stricter password complexity requirements for the `secret` option. If you use encryption (set the `secret` option), your secret must now meet these requirements:
|
|
78
|
+
|
|
79
|
+
- Minimum length: 8 characters
|
|
80
|
+
- Minimum 2 uppercase letters
|
|
81
|
+
- Minimum 2 lowercase letters
|
|
82
|
+
- Minimum 2 numbers
|
|
83
|
+
- Minimum 2 special characters (`!@#$%^&*()_+-=[]{};':"\\|,.<>/?`)
|
|
84
|
+
|
|
85
|
+
**IMPORTANT:** Check if your current secret meets these requirements:
|
|
86
|
+
|
|
87
|
+
- **If your secret already meets the requirements:** You can upgrade safely with no additional action needed.
|
|
88
|
+
- **If your secret does NOT meet the requirements:** You MUST flush all encrypted session data from memcached before upgrading AND update your secret to meet the new requirements. All existing encrypted session data will become inaccessible with the new secret.
|
|
89
|
+
|
|
90
|
+
Sessions without encryption (no `secret` option) are not affected.
|
|
91
|
+
|
|
92
|
+
## Upgrading to v2.x.x
|
|
93
|
+
|
|
94
|
+
When upgrading from pre v2 and using data encryption please flush all the session entries from memcached before rolling the update.
|
|
60
95
|
|
|
61
|
-
|
|
96
|
+
Sessions without data encryption are not affected.
|
|
62
97
|
|
|
63
98
|
## Upgrading from v0.x.x -> v1.x.x
|
|
64
99
|
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
services:
|
|
2
|
+
memcached:
|
|
3
|
+
image: memcached:1.6-alpine
|
|
4
|
+
ports:
|
|
5
|
+
- "11211:11211"
|
|
6
|
+
command: memcached -m 64
|
|
7
|
+
|
|
8
|
+
test-node-14:
|
|
9
|
+
image: node:14-alpine
|
|
10
|
+
depends_on:
|
|
11
|
+
- memcached
|
|
12
|
+
working_dir: /app
|
|
13
|
+
volumes:
|
|
14
|
+
- .:/app
|
|
15
|
+
environment:
|
|
16
|
+
- NODE_ENV=test
|
|
17
|
+
- MEMCACHED_HOST=memcached:11211
|
|
18
|
+
- NPM_CONFIG_FUND=false
|
|
19
|
+
- NPM_CONFIG_UPDATE_NOTIFIER=false
|
|
20
|
+
command: sh -c "npm install && npm test"
|
|
21
|
+
|
|
22
|
+
test-node-16:
|
|
23
|
+
image: node:16-alpine
|
|
24
|
+
depends_on:
|
|
25
|
+
- memcached
|
|
26
|
+
working_dir: /app
|
|
27
|
+
volumes:
|
|
28
|
+
- .:/app
|
|
29
|
+
environment:
|
|
30
|
+
- NODE_ENV=test
|
|
31
|
+
- MEMCACHED_HOST=memcached:11211
|
|
32
|
+
- NPM_CONFIG_FUND=false
|
|
33
|
+
- NPM_CONFIG_UPDATE_NOTIFIER=false
|
|
34
|
+
command: sh -c "npm install && npm test"
|
|
35
|
+
|
|
36
|
+
test-node-18:
|
|
37
|
+
image: node:18-alpine
|
|
38
|
+
depends_on:
|
|
39
|
+
- memcached
|
|
40
|
+
working_dir: /app
|
|
41
|
+
volumes:
|
|
42
|
+
- .:/app
|
|
43
|
+
environment:
|
|
44
|
+
- NODE_ENV=test
|
|
45
|
+
- MEMCACHED_HOST=memcached:11211
|
|
46
|
+
- NPM_CONFIG_FUND=false
|
|
47
|
+
- NPM_CONFIG_UPDATE_NOTIFIER=false
|
|
48
|
+
command: sh -c "npm install && npm test"
|
|
49
|
+
|
|
50
|
+
test-node-20:
|
|
51
|
+
image: node:20-alpine
|
|
52
|
+
depends_on:
|
|
53
|
+
- memcached
|
|
54
|
+
working_dir: /app
|
|
55
|
+
volumes:
|
|
56
|
+
- .:/app
|
|
57
|
+
environment:
|
|
58
|
+
- NODE_ENV=test
|
|
59
|
+
- MEMCACHED_HOST=memcached:11211
|
|
60
|
+
- NPM_CONFIG_FUND=false
|
|
61
|
+
- NPM_CONFIG_UPDATE_NOTIFIER=false
|
|
62
|
+
command: sh -c "npm install && npm test"
|
|
63
|
+
|
|
64
|
+
test-node-22:
|
|
65
|
+
image: node:22-alpine
|
|
66
|
+
depends_on:
|
|
67
|
+
- memcached
|
|
68
|
+
working_dir: /app
|
|
69
|
+
volumes:
|
|
70
|
+
- .:/app
|
|
71
|
+
environment:
|
|
72
|
+
- NODE_ENV=test
|
|
73
|
+
- MEMCACHED_HOST=memcached:11211
|
|
74
|
+
- NPM_CONFIG_FUND=false
|
|
75
|
+
- NPM_CONFIG_UPDATE_NOTIFIER=false
|
|
76
|
+
command: sh -c "npm install && npm test"
|
|
77
|
+
|
|
78
|
+
test-node-24:
|
|
79
|
+
image: node:24-alpine
|
|
80
|
+
depends_on:
|
|
81
|
+
- memcached
|
|
82
|
+
working_dir: /app
|
|
83
|
+
volumes:
|
|
84
|
+
- .:/app
|
|
85
|
+
environment:
|
|
86
|
+
- NODE_ENV=test
|
|
87
|
+
- MEMCACHED_HOST=memcached:11211
|
|
88
|
+
- NPM_CONFIG_FUND=false
|
|
89
|
+
- NPM_CONFIG_UPDATE_NOTIFIER=false
|
|
90
|
+
command: sh -c "npm install && npm test"
|
|
91
|
+
|
|
92
|
+
test-node-25:
|
|
93
|
+
image: node:25-alpine
|
|
94
|
+
depends_on:
|
|
95
|
+
- memcached
|
|
96
|
+
working_dir: /app
|
|
97
|
+
volumes:
|
|
98
|
+
- .:/app
|
|
99
|
+
environment:
|
|
100
|
+
- NODE_ENV=test
|
|
101
|
+
- MEMCACHED_HOST=memcached:11211
|
|
102
|
+
- NPM_CONFIG_FUND=false
|
|
103
|
+
- NPM_CONFIG_UPDATE_NOTIFIER=false
|
|
104
|
+
command: sh -c "npm install && npm test"
|
package/flake.lock
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"nodes": {
|
|
3
|
+
"flake-utils": {
|
|
4
|
+
"inputs": {
|
|
5
|
+
"systems": "systems"
|
|
6
|
+
},
|
|
7
|
+
"locked": {
|
|
8
|
+
"lastModified": 1731533236,
|
|
9
|
+
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
|
10
|
+
"owner": "numtide",
|
|
11
|
+
"repo": "flake-utils",
|
|
12
|
+
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
|
13
|
+
"type": "github"
|
|
14
|
+
},
|
|
15
|
+
"original": {
|
|
16
|
+
"owner": "numtide",
|
|
17
|
+
"repo": "flake-utils",
|
|
18
|
+
"type": "github"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"nixpkgs": {
|
|
22
|
+
"locked": {
|
|
23
|
+
"lastModified": 1765779637,
|
|
24
|
+
"narHash": "sha256-KJ2wa/BLSrTqDjbfyNx70ov/HdgNBCBBSQP3BIzKnv4=",
|
|
25
|
+
"owner": "NixOS",
|
|
26
|
+
"repo": "nixpkgs",
|
|
27
|
+
"rev": "1306659b587dc277866c7b69eb97e5f07864d8c4",
|
|
28
|
+
"type": "github"
|
|
29
|
+
},
|
|
30
|
+
"original": {
|
|
31
|
+
"owner": "NixOS",
|
|
32
|
+
"ref": "nixos-unstable",
|
|
33
|
+
"repo": "nixpkgs",
|
|
34
|
+
"type": "github"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"root": {
|
|
38
|
+
"inputs": {
|
|
39
|
+
"flake-utils": "flake-utils",
|
|
40
|
+
"nixpkgs": "nixpkgs"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"systems": {
|
|
44
|
+
"locked": {
|
|
45
|
+
"lastModified": 1681028828,
|
|
46
|
+
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
47
|
+
"owner": "nix-systems",
|
|
48
|
+
"repo": "default",
|
|
49
|
+
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
50
|
+
"type": "github"
|
|
51
|
+
},
|
|
52
|
+
"original": {
|
|
53
|
+
"owner": "nix-systems",
|
|
54
|
+
"repo": "default",
|
|
55
|
+
"type": "github"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"root": "root",
|
|
60
|
+
"version": 7
|
|
61
|
+
}
|
package/flake.nix
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
description = "Memcached session store for Connect";
|
|
3
|
+
|
|
4
|
+
inputs = {
|
|
5
|
+
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
|
6
|
+
flake-utils.url = "github:numtide/flake-utils";
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
outputs = { self, nixpkgs, flake-utils }:
|
|
10
|
+
flake-utils.lib.eachDefaultSystem (system:
|
|
11
|
+
let
|
|
12
|
+
pkgs = nixpkgs.legacyPackages.${system};
|
|
13
|
+
in
|
|
14
|
+
{
|
|
15
|
+
devShells.default = pkgs.mkShell {
|
|
16
|
+
packages = with pkgs; [
|
|
17
|
+
nodejs
|
|
18
|
+
nodePackages.npm
|
|
19
|
+
memcached
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
shellHook = ''
|
|
23
|
+
echo "connect-memcached development environment"
|
|
24
|
+
echo "Node: $(node --version)"
|
|
25
|
+
echo "npm: $(npm --version)"
|
|
26
|
+
echo ""
|
|
27
|
+
|
|
28
|
+
# Install dependencies if node_modules doesn't exist
|
|
29
|
+
if [ ! -d "node_modules" ]; then
|
|
30
|
+
echo "Installing npm dependencies..."
|
|
31
|
+
npm install
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
echo "Ready! Run 'npm test' to run tests"
|
|
35
|
+
'';
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
}
|
package/index.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
module.exports = require('./lib/connect-memcached');
|
|
1
|
+
module.exports = require('./lib/connect-memcached');
|
package/lib/connect-memcached.js
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
* connect-memcached
|
|
3
3
|
* MIT Licensed
|
|
4
4
|
*/
|
|
5
|
-
const bufferFrom = require('buffer-from');
|
|
6
|
-
|
|
7
5
|
var Memcached = require("memcached");
|
|
8
6
|
var oneDay = 86400;
|
|
9
7
|
|
|
@@ -39,16 +37,17 @@ module.exports = function(session) {
|
|
|
39
37
|
if (!options.hosts) {
|
|
40
38
|
options.hosts = "127.0.0.1:11211";
|
|
41
39
|
}
|
|
42
|
-
if (options.secret) {
|
|
43
|
-
(this.crypto = require("crypto")), (this.secret = options.secret);
|
|
44
|
-
}
|
|
45
|
-
if (options.algorithm) {
|
|
46
|
-
this.algorithm = options.algorithm;
|
|
47
|
-
}
|
|
48
40
|
|
|
49
41
|
options.client = new Memcached(options.hosts, options);
|
|
50
42
|
}
|
|
51
43
|
|
|
44
|
+
if (options.secret) {
|
|
45
|
+
options.algorithm = options.algorithm || 'aes-256-gcm';
|
|
46
|
+
options.hashing = options.hashing || 'sha512';
|
|
47
|
+
this.kruptein = require("kruptein")(options);
|
|
48
|
+
this.secret = options.secret;
|
|
49
|
+
}
|
|
50
|
+
|
|
52
51
|
this.client = options.client;
|
|
53
52
|
}
|
|
54
53
|
|
|
@@ -72,7 +71,8 @@ module.exports = function(session) {
|
|
|
72
71
|
* @api public
|
|
73
72
|
*/
|
|
74
73
|
MemcachedStore.prototype.get = function(sid, fn) {
|
|
75
|
-
|
|
74
|
+
var self = this, sid = this.getKey(sid),
|
|
75
|
+
parseable_string;
|
|
76
76
|
|
|
77
77
|
this.client.get(sid, function(err, data) {
|
|
78
78
|
if (err) {
|
|
@@ -82,13 +82,20 @@ module.exports = function(session) {
|
|
|
82
82
|
if (!data) {
|
|
83
83
|
return fn();
|
|
84
84
|
}
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
|
|
86
|
+
if (self.secret) {
|
|
87
|
+
self.kruptein.get(self.secret, data, function(err, ct) {
|
|
88
|
+
if (err) {
|
|
89
|
+
return fn(err, {});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
parseable_string = JSON.parse(ct);
|
|
93
|
+
});
|
|
87
94
|
} else {
|
|
88
|
-
parseable_string = data
|
|
95
|
+
parseable_string = data;
|
|
89
96
|
}
|
|
90
97
|
|
|
91
|
-
|
|
98
|
+
fn(null, parseable_string);
|
|
92
99
|
} catch (e) {
|
|
93
100
|
fn(e);
|
|
94
101
|
}
|
|
@@ -110,16 +117,16 @@ module.exports = function(session) {
|
|
|
110
117
|
var maxAge = sess.cookie.maxAge;
|
|
111
118
|
var ttl =
|
|
112
119
|
this.ttl || ("number" == typeof maxAge ? (maxAge / 1000) | 0 : oneDay);
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
|
|
121
|
+
if (this.secret) {
|
|
122
|
+
this.kruptein.set(this.secret, sess, function(err, ct) {
|
|
123
|
+
if (err) {
|
|
124
|
+
return fn(err);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
sess = ct;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
123
130
|
|
|
124
131
|
this.client.set(sid, sess, ttl, ensureCallback(fn));
|
|
125
132
|
} catch (err) {
|
|
@@ -172,70 +179,5 @@ module.exports = function(session) {
|
|
|
172
179
|
this.set(sid, sess, fn);
|
|
173
180
|
};
|
|
174
181
|
|
|
175
|
-
function encryptData(plaintext) {
|
|
176
|
-
var pt = encrypt.call(this, this.secret, plaintext, this.algo),
|
|
177
|
-
hmac = digest.call(this, this.secret, pt);
|
|
178
|
-
|
|
179
|
-
return {
|
|
180
|
-
ct: pt,
|
|
181
|
-
mac: hmac
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function decryptData(ciphertext) {
|
|
186
|
-
ciphertext = JSON.parse(ciphertext);
|
|
187
|
-
|
|
188
|
-
var hmac = digest.call(this, this.secret, ciphertext.ct);
|
|
189
|
-
|
|
190
|
-
if (hmac != ciphertext.mac) {
|
|
191
|
-
throw "Encrypted session was tampered with!";
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return decrypt.call(this, this.secret, ciphertext.ct, this.algo);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function digest(key, obj) {
|
|
198
|
-
var hmac = this.crypto.createHmac("sha512", key);
|
|
199
|
-
hmac.setEncoding("hex");
|
|
200
|
-
hmac.write(obj);
|
|
201
|
-
hmac.end();
|
|
202
|
-
return hmac.read();
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
function encrypt(key, pt, algo) {
|
|
206
|
-
algo = algo || "aes-256-ctr";
|
|
207
|
-
pt = Buffer.isBuffer(pt) ? pt : new bufferFrom(pt);
|
|
208
|
-
var iv = this.crypto.randomBytes(16);
|
|
209
|
-
var hashedKey = this.crypto
|
|
210
|
-
.createHash("sha256")
|
|
211
|
-
.update(key)
|
|
212
|
-
.digest();
|
|
213
|
-
var cipher = this.crypto.createCipheriv(algo, hashedKey, iv),
|
|
214
|
-
ct = [];
|
|
215
|
-
ct.push(iv.toString("hex"));
|
|
216
|
-
ct.push(cipher.update(pt, "buffer", "hex"));
|
|
217
|
-
ct.push(cipher.final("hex"));
|
|
218
|
-
|
|
219
|
-
return ct.join("");
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function decrypt(key, ct, algo) {
|
|
223
|
-
algo = algo || "aes-256-ctr";
|
|
224
|
-
var dataBuffer = bufferFrom(ct, "hex");
|
|
225
|
-
var iv = dataBuffer.slice(0, 16);
|
|
226
|
-
var hashedKey = this.crypto
|
|
227
|
-
.createHash("sha256")
|
|
228
|
-
.update(key)
|
|
229
|
-
.digest();
|
|
230
|
-
|
|
231
|
-
var cipher = this.crypto.createDecipheriv(algo, hashedKey, iv),
|
|
232
|
-
pt = [];
|
|
233
|
-
|
|
234
|
-
pt.push(cipher.update(dataBuffer.slice(16), "hex", "utf8"));
|
|
235
|
-
pt.push(cipher.final("utf8"));
|
|
236
|
-
|
|
237
|
-
return pt.join("");
|
|
238
|
-
}
|
|
239
|
-
|
|
240
182
|
return MemcachedStore;
|
|
241
183
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "connect-memcached",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Memcached session store for Connect",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"memcached",
|
|
@@ -15,11 +15,26 @@
|
|
|
15
15
|
"url": "https://github.com/balor/connect-memcached"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"
|
|
18
|
+
"kruptein": "3.1.x",
|
|
19
19
|
"memcached": "2.2.x"
|
|
20
20
|
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"express": "^4.17.3",
|
|
23
|
+
"express-session": "^1.17.2",
|
|
24
|
+
"jest": "^27.5.1",
|
|
25
|
+
"supertest": "^6.2.2"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"test": "NODE_ENV=test jest --testTimeout=10000"
|
|
29
|
+
},
|
|
30
|
+
"jest": {
|
|
31
|
+
"testEnvironment": "node",
|
|
32
|
+
"coveragePathIgnorePatterns": [
|
|
33
|
+
"/node_modules/"
|
|
34
|
+
]
|
|
35
|
+
},
|
|
21
36
|
"engines": {
|
|
22
|
-
"node": ">= 0.
|
|
37
|
+
"node": ">= 14.0.0"
|
|
23
38
|
},
|
|
24
39
|
"license": "MIT",
|
|
25
40
|
"directories": {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const express = require("express"),
|
|
2
|
+
session = require("express-session"),
|
|
3
|
+
app = express(),
|
|
4
|
+
MemcachedStore = require("../../lib/connect-memcached")(session);
|
|
5
|
+
|
|
6
|
+
const memcachedStore = new MemcachedStore({
|
|
7
|
+
hosts: [process.env.MEMCACHED_HOST || "127.0.0.1:11211"],
|
|
8
|
+
prefix: "testapp_",
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
app.use(
|
|
12
|
+
session({
|
|
13
|
+
secret: "TestSecret",
|
|
14
|
+
key: "test",
|
|
15
|
+
proxy: "true",
|
|
16
|
+
resave: false,
|
|
17
|
+
saveUninitialized: false,
|
|
18
|
+
store: memcachedStore,
|
|
19
|
+
})
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
app.get("/", function (req, res) {
|
|
23
|
+
if (req.session.views) {
|
|
24
|
+
++req.session.views;
|
|
25
|
+
} else {
|
|
26
|
+
req.session.views = 1;
|
|
27
|
+
}
|
|
28
|
+
res.json({ pageviews: req.session.views });
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (process.env.NODE_ENV !== "test") {
|
|
32
|
+
app.listen(9341, function () {
|
|
33
|
+
console.log("Listening on %d", this.address().port);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = {
|
|
38
|
+
app: app,
|
|
39
|
+
memcachedStore: memcachedStore,
|
|
40
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const express = require("express"),
|
|
2
|
+
session = require("express-session"),
|
|
3
|
+
app = express(),
|
|
4
|
+
MemcachedStore = require("../../lib/connect-memcached")(session);
|
|
5
|
+
|
|
6
|
+
const memcachedStore = new MemcachedStore({
|
|
7
|
+
hosts: [process.env.MEMCACHED_HOST || "127.0.0.1:11211"],
|
|
8
|
+
secret: "Hello There Stranger2025!?",
|
|
9
|
+
prefix: "testapp_encrypt_",
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
app.use(
|
|
13
|
+
session({
|
|
14
|
+
secret: "TestEncryptSecret",
|
|
15
|
+
key: "test_encrypt",
|
|
16
|
+
proxy: "true",
|
|
17
|
+
resave: false,
|
|
18
|
+
saveUninitialized: false,
|
|
19
|
+
store: memcachedStore,
|
|
20
|
+
})
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
app.get("/", function (req, res) {
|
|
24
|
+
if (req.session.views) {
|
|
25
|
+
++req.session.views;
|
|
26
|
+
} else {
|
|
27
|
+
req.session.views = 1;
|
|
28
|
+
}
|
|
29
|
+
res.json({ pageviews: req.session.views });
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
if (process.env.NODE_ENV !== "test") {
|
|
33
|
+
app.listen(9341, function () {
|
|
34
|
+
console.log("Listening on %d", this.address().port);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = {
|
|
39
|
+
app: app,
|
|
40
|
+
memcachedStore: memcachedStore,
|
|
41
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const express = require("express"),
|
|
2
|
+
session = require("express-session"),
|
|
3
|
+
app = express(),
|
|
4
|
+
Memcached = require("memcached"),
|
|
5
|
+
MemcachedStore = require("../../lib/connect-memcached")(session);
|
|
6
|
+
|
|
7
|
+
const memcachedClient = new Memcached(process.env.MEMCACHED_HOST || "127.0.0.1:11211");
|
|
8
|
+
|
|
9
|
+
const memcachedStore = new MemcachedStore({
|
|
10
|
+
client: memcachedClient,
|
|
11
|
+
prefix: "testapp_encrypt_",
|
|
12
|
+
secret: "Hello There Stranger2025!?",
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
app.use(
|
|
16
|
+
session({
|
|
17
|
+
secret: "TestEncryptSecret",
|
|
18
|
+
key: "test_encrypt",
|
|
19
|
+
proxy: "true",
|
|
20
|
+
resave: false,
|
|
21
|
+
saveUninitialized: false,
|
|
22
|
+
store: memcachedStore,
|
|
23
|
+
})
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
app.get("/", function (req, res) {
|
|
27
|
+
if (req.session.views) {
|
|
28
|
+
++req.session.views;
|
|
29
|
+
} else {
|
|
30
|
+
req.session.views = 1;
|
|
31
|
+
}
|
|
32
|
+
res.json({ pageviews: req.session.views });
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (process.env.NODE_ENV !== "test") {
|
|
36
|
+
app.listen(9341, function () {
|
|
37
|
+
console.log("Listening on %d", this.address().port);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = {
|
|
42
|
+
app: app,
|
|
43
|
+
memcachedStore: memcachedStore,
|
|
44
|
+
};
|
package/test/test.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const supertest = require("supertest");
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
describe("Plain memcached session store", () => {
|
|
5
|
+
const { app, memcachedStore } = require("./services/memcached_basic.js");
|
|
6
|
+
const serverAgent = supertest.agent(app);
|
|
7
|
+
|
|
8
|
+
it("GET / should increment views value at each request", async () => {
|
|
9
|
+
let lastPageView = 0;
|
|
10
|
+
for (var i = 1; i < 50; i++) {
|
|
11
|
+
const res = await serverAgent.get("/");
|
|
12
|
+
expect(res.status).toEqual(200);
|
|
13
|
+
expect(res.type).toEqual(expect.stringContaining("json"));
|
|
14
|
+
expect(res.body.pageviews).toBeGreaterThan(lastPageView);
|
|
15
|
+
lastPageView = res.body.pageviews;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterAll(() => {
|
|
20
|
+
memcachedStore.client.end();
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe("Encrypted memcached session store", () => {
|
|
25
|
+
const { app, memcachedStore } = require("./services/memcached_crypt.js");
|
|
26
|
+
const serverAgent = supertest.agent(app);
|
|
27
|
+
|
|
28
|
+
it("GET / should increment views value at each request", async () => {
|
|
29
|
+
let lastPageView = 0;
|
|
30
|
+
for (var i = 1; i < 50; i++) {
|
|
31
|
+
const res = await serverAgent.get("/");
|
|
32
|
+
expect(res.status).toEqual(200);
|
|
33
|
+
expect(res.type).toEqual(expect.stringContaining("json"));
|
|
34
|
+
expect(res.body.pageviews).toBeGreaterThan(lastPageView);
|
|
35
|
+
lastPageView = res.body.pageviews;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
afterAll(() => {
|
|
40
|
+
memcachedStore.client.end();
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe("Encrypted session store using preexising memcached client", () => {
|
|
45
|
+
const { app, memcachedStore } = require("./services/memcached_preexisting_crypt_connection.js");
|
|
46
|
+
const serverAgent = supertest.agent(app);
|
|
47
|
+
|
|
48
|
+
it("GET / should increment views value at each request", async () => {
|
|
49
|
+
let lastPageView = 0;
|
|
50
|
+
for (var i = 1; i < 50; i++) {
|
|
51
|
+
const res = await serverAgent.get("/");
|
|
52
|
+
expect(res.status).toEqual(200);
|
|
53
|
+
expect(res.type).toEqual(expect.stringContaining("json"));
|
|
54
|
+
expect(res.body.pageviews).toBeGreaterThan(lastPageView);
|
|
55
|
+
expect(memcachedStore.kruptein.crypto).toBe(require("crypto"));
|
|
56
|
+
lastPageView = res.body.pageviews;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
afterAll(() => {
|
|
61
|
+
memcachedStore.client.end();
|
|
62
|
+
});
|
|
63
|
+
});
|
package/test-all.sh
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
set -e
|
|
4
|
+
|
|
5
|
+
echo "Starting memcached..."
|
|
6
|
+
docker-compose up -d memcached
|
|
7
|
+
sleep 2
|
|
8
|
+
|
|
9
|
+
NODE_VERSIONS=(14 16 18 20 22 24 25)
|
|
10
|
+
FAILED=()
|
|
11
|
+
|
|
12
|
+
for version in "${NODE_VERSIONS[@]}"; do
|
|
13
|
+
echo ""
|
|
14
|
+
echo "========================================="
|
|
15
|
+
echo "Testing with Node $version"
|
|
16
|
+
echo "========================================="
|
|
17
|
+
|
|
18
|
+
if docker-compose run --rm test-node-$version; then
|
|
19
|
+
echo "✓ Node $version: PASSED"
|
|
20
|
+
else
|
|
21
|
+
echo "✗ Node $version: FAILED"
|
|
22
|
+
FAILED+=($version)
|
|
23
|
+
fi
|
|
24
|
+
done
|
|
25
|
+
|
|
26
|
+
echo ""
|
|
27
|
+
echo "========================================="
|
|
28
|
+
echo "Test Summary"
|
|
29
|
+
echo "========================================="
|
|
30
|
+
|
|
31
|
+
if [ ${#FAILED[@]} -eq 0 ]; then
|
|
32
|
+
echo "✓ All Node versions passed!"
|
|
33
|
+
docker-compose down
|
|
34
|
+
exit 0
|
|
35
|
+
else
|
|
36
|
+
echo "✗ Failed Node versions: ${FAILED[*]}"
|
|
37
|
+
docker-compose down
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
package/.npmignore
DELETED
package/tests/test.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
var express = require("express"),
|
|
2
|
-
session = require("express-session"),
|
|
3
|
-
cookieParser = require("cookie-parser"),
|
|
4
|
-
http = require("http"),
|
|
5
|
-
app = express(),
|
|
6
|
-
MemcachedStore = require("../lib/connect-memcached")(session);
|
|
7
|
-
|
|
8
|
-
app.use(cookieParser());
|
|
9
|
-
app.use(
|
|
10
|
-
session({
|
|
11
|
-
secret: "TestSecret",
|
|
12
|
-
key: "test",
|
|
13
|
-
proxy: "true",
|
|
14
|
-
resave: false,
|
|
15
|
-
saveUninitialized: false,
|
|
16
|
-
store: new MemcachedStore({
|
|
17
|
-
hosts: ["127.0.0.1:11211"],
|
|
18
|
-
prefix: "testapp_"
|
|
19
|
-
})
|
|
20
|
-
})
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
app.get("/", function(req, res) {
|
|
24
|
-
if (req.session.views) {
|
|
25
|
-
++req.session.views;
|
|
26
|
-
} else {
|
|
27
|
-
req.session.views = 1;
|
|
28
|
-
}
|
|
29
|
-
res.send("Viewed <strong>" + req.session.views + "</strong> times.");
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
http.createServer(app).listen(9341, function() {
|
|
33
|
-
console.log("Listening on %d", this.address().port);
|
|
34
|
-
});
|
package/tests/test_encrypt.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
var express = require("express"),
|
|
2
|
-
session = require("express-session"),
|
|
3
|
-
cookieParser = require("cookie-parser"),
|
|
4
|
-
http = require("http"),
|
|
5
|
-
app = express(),
|
|
6
|
-
MemcachedStore = require("../lib/connect-memcached")(session);
|
|
7
|
-
|
|
8
|
-
app.use(cookieParser());
|
|
9
|
-
app.use(
|
|
10
|
-
session({
|
|
11
|
-
secret: "TestEncryptSecret",
|
|
12
|
-
key: "test_encrypt",
|
|
13
|
-
proxy: "true",
|
|
14
|
-
resave: false,
|
|
15
|
-
saveUninitialized: false,
|
|
16
|
-
store: new MemcachedStore({
|
|
17
|
-
hosts: ["127.0.0.1:11211"],
|
|
18
|
-
secret: "Hello there stranger!",
|
|
19
|
-
prefix: "testapp_encrypt_"
|
|
20
|
-
})
|
|
21
|
-
})
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
app.get("/", function(req, res) {
|
|
25
|
-
if (req.session.views) {
|
|
26
|
-
++req.session.views;
|
|
27
|
-
} else {
|
|
28
|
-
req.session.views = 1;
|
|
29
|
-
}
|
|
30
|
-
res.send("Viewed <strong>" + req.session.views + "</strong> times.");
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
http.createServer(app).listen(9341, function() {
|
|
34
|
-
console.log("Listening on %d", this.address().port);
|
|
35
|
-
});
|