Skip to content

Commit 0366566

Browse files
committed
Move session handling code into a SessionManager class
1 parent f587637 commit 0366566

File tree

13 files changed

+95
-79
lines changed

13 files changed

+95
-79
lines changed

.changeset/e3ae8a80/changes.json

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"releases": [
3+
{ "name": "@voussoir/admin-ui", "type": "patch" },
4+
{ "name": "@voussoir/core", "type": "major" },
5+
{ "name": "@voussoir/fields", "type": "patch" },
6+
{ "name": "@voussoir/server", "type": "patch" },
7+
{ "name": "@voussoir/cypress-project-basic", "type": "patch" },
8+
{ "name": "@voussoir/cypress-project-login", "type": "patch" },
9+
{ "name": "@voussoir/cypress-project-twitter-login", "type": "patch" }
10+
],
11+
"dependents": [
12+
{ "name": "@voussoir/adapter-mongoose", "type": "patch", "dependencies": ["@voussoir/core"] },
13+
{
14+
"name": "@voussoir/test-utils",
15+
"type": "patch",
16+
"dependencies": ["@voussoir/adapter-mongoose", "@voussoir/core", "@voussoir/server"]
17+
},
18+
{
19+
"name": "@voussoir/cypress-project-access-control",
20+
"type": "patch",
21+
"dependencies": [
22+
"@voussoir/adapter-mongoose",
23+
"@voussoir/test-utils",
24+
"@voussoir/admin-ui",
25+
"@voussoir/core",
26+
"@voussoir/fields",
27+
"@voussoir/server"
28+
]
29+
}
30+
]
31+
}

.changeset/e3ae8a80/changes.md

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- Rename keystone.session to keystone.sessionManager
2+
- Rename keystone.session.validate to keystone.sessionManager.populateAuthedItemMiddleware
3+
- Rename keystone.session.create to keystone.sessionManager.startAuthedSession
4+
- Rename keystone.session.destroy to keystone.sessionManager.endAuthedSession

packages/admin-ui/server/AdminUI.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ module.exports = class AdminUI {
5555
});
5656
}
5757

58-
await this.keystone.session.create(req, result);
58+
await this.keystone.sessionManager.startAuthedSession(req, result);
5959
} catch (e) {
6060
return next(e);
6161
}
@@ -75,7 +75,7 @@ module.exports = class AdminUI {
7575
async signout(req, res, next) {
7676
let success;
7777
try {
78-
await this.keystone.session.destroy(req);
78+
await this.keystone.sessionManager.endAuthedSession(req);
7979
success = true;
8080
} catch (e) {
8181
success = false;

packages/admin-ui/tests/server/AdminUI.test.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@ jest.doMock('html-webpack-plugin', () => {
1919
const AdminUI = require('../../server/AdminUI.js');
2020

2121
const keystone = {
22-
session: {
23-
validate: () => jest.fn(),
24-
},
22+
sessionManager: {},
2523
getAdminSchema: jest.fn(),
2624
getAdminMeta: jest.fn(),
2725
};

packages/core/Keystone/index.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const {
1414
mergeRelationships,
1515
} = require('./relationship-utils');
1616
const List = require('../List');
17-
const bindSession = require('./session');
17+
const SessionManager = require('./session');
1818

1919
const unique = arr => [...new Set(arr)];
2020

@@ -30,7 +30,7 @@ module.exports = class Keystone {
3030
this.lists = {};
3131
this.listsArray = [];
3232
this.getListByKey = key => this.lists[key];
33-
this.session = bindSession(this);
33+
this.sessionManager = new SessionManager(this);
3434

3535
if (config.adapters) {
3636
this.adapters = config.adapters;

packages/core/Keystone/session.js

+42-41
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,48 @@
1-
const noop = () => undefined;
2-
3-
const validate = keystone => ({ valid = noop, invalid = noop }) => async (req, res, next) => {
4-
if (!req.session || !req.session.keystoneItemId) {
5-
invalid({ req, reason: 'empty' });
6-
return next();
1+
class SessionManager {
2+
constructor(keystone) {
3+
this.keystone = keystone;
4+
this.populateAuthedItemMiddleware = this.populateAuthedItemMiddleware.bind(this);
75
}
8-
const list = keystone.lists[req.session.keystoneListKey];
9-
if (!list) {
10-
// TODO: probably destroy the session
11-
invalid({ req, reason: 'invalid-list' });
12-
return next();
6+
7+
async populateAuthedItemMiddleware(req, res, next) {
8+
if (!req.session || !req.session.keystoneItemId) {
9+
return next();
10+
}
11+
const list = this.keystone.lists[req.session.keystoneListKey];
12+
if (!list) {
13+
// TODO: probably destroy the session
14+
return next();
15+
}
16+
const item = await list.adapter.findById(req.session.keystoneItemId);
17+
if (!item) {
18+
// TODO: probably destroy the session
19+
return next();
20+
}
21+
req.user = item;
22+
req.authedListKey = list.key;
23+
24+
next();
1325
}
14-
const item = await list.adapter.findById(req.session.keystoneItemId);
15-
if (!item) {
16-
// TODO: probably destroy the session
17-
invalid({ req, reason: 'invalid-item' });
18-
return next();
26+
27+
startAuthedSession(req, { item, list }) {
28+
return new Promise((resolve, reject) =>
29+
req.session.regenerate(err => {
30+
if (err) return reject(err);
31+
req.session.keystoneListKey = list.key;
32+
req.session.keystoneItemId = item.id;
33+
resolve();
34+
})
35+
);
1936
}
20-
valid({ req, list, item });
21-
next();
22-
};
2337

24-
function create(req, { item, list }) {
25-
return new Promise((resolve, reject) =>
26-
req.session.regenerate(err => {
27-
if (err) return reject(err);
28-
req.session.keystoneListKey = list.key;
29-
req.session.keystoneItemId = item.id;
30-
resolve();
31-
})
32-
);
33-
}
34-
function destroy(req) {
35-
return new Promise((resolve, reject) =>
36-
req.session.regenerate(err => {
37-
if (err) return reject(err);
38-
resolve({ success: true });
39-
})
40-
);
38+
endAuthedSession(req) {
39+
return new Promise((resolve, reject) =>
40+
req.session.regenerate(err => {
41+
if (err) return reject(err);
42+
resolve({ success: true });
43+
})
44+
);
45+
}
4146
}
4247

43-
module.exports = keystone => ({
44-
create: create,
45-
destroy: destroy,
46-
validate: validate(keystone),
47-
});
48+
module.exports = SessionManager;

packages/core/auth/index.md

+6-10
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,8 @@ The time spend verifying an actors credentials should be constant-time regardles
127127

128128
```javascript
129129
server.app.use(
130-
keystone.session.validate({
131-
// When logged in, we'll get a req.user object
132-
valid: ({ req, item }) => (req.user = item),
133-
})
130+
// When logged in, we'll get a req.user object
131+
keystone.sessionManager.populateAuthedItemMiddleware
134132
);
135133

136134
const twitterAuth = keystone.createAuthStrategy({
@@ -168,7 +166,7 @@ server.app.get(
168166

169167
try {
170168
await keystone.auth.User.twitter.connectItem(req, { item: authedItem });
171-
await keystone.session.create(req, {
169+
await keystone.sessionManager.startAuthedSession(req, {
172170
item: authedItem,
173171
list: info.list,
174172
});
@@ -198,10 +196,8 @@ bank's password over multiple server requests / page refreshes:
198196

199197
```javascript
200198
server.app.use(
201-
keystone.session.validate({
202-
// When logged in, we'll get a req.user object
203-
valid: ({ req, item }) => (req.user = item),
204-
})
199+
// When logged in, we'll get a req.user object
200+
keystone.sessionManager.populateAuthedItemMiddleware
205201
);
206202

207203
const twitterAuth = keystone.createAuthStrategy({
@@ -280,7 +276,7 @@ server.app.post('/auth/twitter/complete', async (req, res, next) => {
280276
});
281277

282278
await keystone.auth.User.twitter.connectItem(req, { item });
283-
await keystone.session.create(req, { item, list });
279+
await keystone.sessionManager.startAuthedSession(req, { item, list });
284280
res.redirect('/api/session');
285281
} catch (createError) {
286282
next(createError);

packages/fields/tests/fields.test.js

-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ describe('Test CRUD for all fields', () => {
7777
// Set up a server (but do not .listen(), we will use supertest to access the app)
7878
const server = new WebServer(keystone, {
7979
'cookie secret': 'qwerty',
80-
session: false,
8180
});
8281

8382
return { server, keystone };

packages/server/WebServer/index.js

+1-8
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,7 @@ module.exports = class WebServer {
7878
this.app.use(injectAuthCookieMiddleware, sessionMiddleware);
7979

8080
// Attach the user to the request for all following route handlers
81-
this.app.use(
82-
this.keystone.session.validate({
83-
valid: ({ req, list, item }) => {
84-
req.user = item;
85-
req.authedListKey = list.key;
86-
},
87-
})
88-
);
81+
this.app.use(this.keystone.sessionManager.populateAuthedItemMiddleware);
8982
}
9083

9184
if (adminUI && this.config.authStrategy) {

projects/basic/index.js

-6
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,6 @@ const server = new WebServer(keystone, {
151151
port,
152152
});
153153

154-
server.app.use(
155-
keystone.session.validate({
156-
valid: ({ req, item }) => (req.user = item),
157-
})
158-
);
159-
160154
server.app.get('/reset-db', (req, res) => {
161155
const reset = async () => {
162156
Object.values(keystone.adapters).forEach(async adapter => {

projects/login/index.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ server.app.post(
6565
bodyParser.urlencoded({ extended: true }),
6666
async (req, res, next) => {
6767
// Cleanup any previous session
68-
await keystone.session.destroy(req);
68+
await keystone.sessionManager.endAuthedSession(req);
6969

7070
try {
7171
const result = await keystone.auth.User.password.validate({
@@ -77,7 +77,7 @@ server.app.post(
7777
success: false,
7878
});
7979
}
80-
await keystone.session.create(req, result);
80+
await keystone.sessionManager.startAuthedSession(req, result);
8181
res.json({
8282
success: true,
8383
itemId: result.item.id,
@@ -91,7 +91,7 @@ server.app.post(
9191

9292
server.app.get('/signout', async (req, res, next) => {
9393
try {
94-
await keystone.session.destroy(req);
94+
await keystone.sessionManager.endAuthedSession(req);
9595
res.json({
9696
success: true,
9797
});

projects/login/tests/auth-header.test.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ function setupKeystone() {
4848
bodyParser.urlencoded({ extended: true }),
4949
async (req, res, next) => {
5050
// Cleanup any previous session
51-
await keystone.session.destroy(req);
51+
await keystone.sessionManager.endAuthedSession(req);
5252

5353
try {
5454
const result = await keystone.auth.User.password.validate({
@@ -60,7 +60,7 @@ function setupKeystone() {
6060
success: false,
6161
});
6262
}
63-
await keystone.session.create(req, result);
63+
await keystone.sessionManager.startAuthedSession(req, result);
6464
res.json({
6565
success: true,
6666
token: req.sessionID,

projects/twitter-login/twitter.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ exports.configureTwitterAuth = function(keystone, server) {
7878
});
7979

8080
await keystone.auth.User.twitter.connectItem(req, { item });
81-
await keystone.session.create(req, { item, list });
81+
await keystone.sessionManager.startAuthedSession(req, { item, list });
8282
res.redirect('/api/session');
8383
} catch (createError) {
8484
next(createError);

0 commit comments

Comments
 (0)