Skip to content

Commit 2d716c6

Browse files
committed
wrap device creation in transaction
1 parent e515d5f commit 2d716c6

File tree

5 files changed

+91
-63
lines changed

5 files changed

+91
-63
lines changed

packages/api/lib/helpers/jwtHelpers.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const { findUserByEmailAndRole } = require('@sensebox/opensensemap-api-models/sr
55
const config = require('config'),
66
jwt = require('jsonwebtoken'),
77
hashJWT = require('./jwtRefreshTokenHasher'),
8-
{ addTokenToBlacklist, addTokenHashToBlacklist, isTokenBlacklisted } = require('./tokenBlacklist'),
8+
{ addTokenToBlacklist, addTokenHashToBlacklist, isTokenBlacklisted, addRefreshTokenToBlacklist } = require('./tokenBlacklist'),
99
{ v4: uuidv4 } = require('uuid'),
1010
moment = require('moment'),
1111
{ User } = require('@sensebox/opensensemap-api-models'),
@@ -68,8 +68,8 @@ const refreshJwt = async function refreshJwt (refreshToken) {
6868
throw new ForbiddenError('Refresh token invalid or too old. Please sign in with your username and password.');
6969
}
7070

71-
// TODO: invalidate old token
72-
// addTokenHashToBlacklist(refreshToken);
71+
// Add the old refresh token to the blacklist
72+
addRefreshTokenToBlacklist(refreshToken);
7373

7474
const { token, refreshToken: newRefreshToken } = await createToken(user);
7575

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,8 @@
11
'use strict';
22

3-
const { insertTokenToBlacklist, findToken } = require('@sensebox/opensensemap-api-models/src/token');
3+
const { insertTokenToBlacklist, findToken, insertTokenToBlacklistWithExpiresAt } = require('@sensebox/opensensemap-api-models/src/token');
44
const hashJWT = require('./jwtRefreshTokenHasher');
5-
6-
// TODO: Move this to pg_cron
7-
// const cleanupExpiredTokens = function cleanupExpiredTokens () {
8-
// const now = Date.now() / 1000;
9-
// for (const jti of Object.keys(tokenBlacklist)) {
10-
// if (tokenBlacklist[jti].exp < now) {
11-
// delete tokenBlacklist[jti];
12-
// }
13-
// }
14-
// };
5+
const moment = require('moment');
156

167
const isTokenBlacklisted = async function isTokenBlacklisted (token, tokenString) {
178
if (!token.jti) { // token has no id.. -> shouldn't be accepted
@@ -37,21 +28,13 @@ const addTokenToBlacklist = function addTokenToBlacklist (token, tokenString) {
3728
}
3829
};
3930

40-
const addTokenHashToBlacklist = function addTokenHashToBlacklist (tokenHash) {
41-
// cleanupExpiredTokens();
42-
43-
// if (typeof tokenHash === 'string') {
44-
// // just set the exp claim to now plus one week to be sure
45-
// tokenBlacklist[tokenHash] = {
46-
// exp: moment.utc()
47-
// .add(1, 'week')
48-
// .unix()
49-
// };
50-
// }
31+
const addRefreshTokenToBlacklist = function addRefreshTokenToBlacklist (refreshToken) {
32+
insertTokenToBlacklistWithExpiresAt('', refreshToken, moment.utc().add(1, 'week')
33+
.unix());
5134
};
5235

5336
module.exports = {
5437
isTokenBlacklisted,
5538
addTokenToBlacklist,
56-
addTokenHashToBlacklist
39+
addRefreshTokenToBlacklist
5740
};

packages/models/src/device/index.js

Lines changed: 71 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
'use strict';
22

33
const crypto = require('crypto');
4-
const { deviceTable, sensorTable, accessTokenTable } = require('../../schema/schema');
4+
const { deviceTable, sensorTable, accessTokenTable, deviceToLocationTable, locationTable } = require('../../schema/schema');
55
const sensorLayouts = require('../box/sensorLayouts');
66
const { db } = require('../drizzle');
77
const ModelError = require('../modelError');
8-
const { inArray, arrayContains, sql, eq, asc, ilike, getTableColumns } = require('drizzle-orm');
8+
const { inArray, arrayContains, sql, eq, asc, ilike } = require('drizzle-orm');
99
const { insertMeasurement, insertMeasurements } = require('../measurement');
1010
const SketchTemplater = require('@sensebox/sketch-templater');
1111

@@ -94,42 +94,78 @@ const createDevice = async function createDevice (userId, params) {
9494
}
9595
}
9696

97-
// TODO: handle in transaction
98-
const [device] = await db
99-
.insert(deviceTable)
100-
.values({
101-
userId,
102-
name,
103-
exposure,
104-
description,
105-
latitude: location[1],
106-
longitude: location[0],
107-
location: sql`ST_SetSRID(ST_MakePoint(${location[1]}, ${location[0]}), 4326)`,
108-
useAuth,
109-
model,
110-
tags: grouptag
111-
})
112-
.returning();
113-
114-
const [accessToken] = await db.insert(accessTokenTable).values({
115-
deviceId: device.id,
116-
token: crypto.randomBytes(32).toString('hex')
117-
})
118-
.returning({ token: accessTokenTable.token });
119-
120-
// Iterate over sensors and add device id
121-
sensors = sensors.map((sensor) => ({
122-
deviceId: device.id,
123-
...sensor
124-
}));
125-
126-
const deviceSensors = await db.insert(sensorTable).values(sensors)
127-
.returning();
97+
// Handle everything in a transaction to ensure consistency
98+
const device = await db.transaction(async (tx) => {
99+
const [device] = await tx
100+
.insert(deviceTable)
101+
.values({
102+
userId,
103+
name,
104+
exposure,
105+
description,
106+
useAuth,
107+
model,
108+
latitude: location[1],
109+
longitude: location[0],
110+
tags: grouptag
111+
})
112+
.returning();
113+
114+
const [geometry] = await tx
115+
.insert(locationTable)
116+
.values({
117+
location: sql`ST_SetSRID(ST_MakePoint(${location[1]}, ${location[0]}), 4326)`
118+
})
119+
.onConflictDoNothing()
120+
.returning({ id: locationTable.id });
121+
122+
if (geometry) {
123+
// Create location relation
124+
await tx
125+
.insert(deviceToLocationTable)
126+
.values({ deviceId: device.id, locationId: geometry.id });
127+
} else {
128+
// Get location id
129+
const geom = await tx.query.locationTable.findFirst({
130+
columns: {
131+
id: true
132+
},
133+
where: sql`ST_Equals(${locationTable.location}, ST_SetSRID(ST_MakePoint(${location[1]}, ${location[0]}), 4326))`
134+
});
135+
136+
// Create location relation
137+
await tx
138+
.insert(deviceToLocationTable)
139+
.values({ deviceId: device.id, locationId: geom.id });
140+
}
128141

129-
device['accessToken'] = accessToken.token;
130-
device['sensors'] = deviceSensors;
142+
const [accessToken] = await tx
143+
.insert(accessTokenTable)
144+
.values({
145+
deviceId: device.id,
146+
token: crypto.randomBytes(32).toString('hex')
147+
})
148+
.returning({ token: accessTokenTable.token });
149+
150+
// Iterate over sensors and add device id
151+
sensors = sensors.map((sensor) => ({
152+
deviceId: device.id,
153+
...sensor
154+
}));
155+
156+
const deviceSensors = await tx
157+
.insert(sensorTable)
158+
.values(sensors)
159+
.returning();
160+
161+
device['accessToken'] = accessToken.token;
162+
device['sensors'] = deviceSensors;
163+
164+
return device;
165+
});
131166

132167
return device;
168+
133169
};
134170

135171
const deleteDevice = async function (filter) {

packages/models/src/token/index.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ const insertTokenToBlacklist = async function (hash, token) {
1313
});
1414
};
1515

16+
const insertTokenToBlacklistWithExpiresAt = async function (hash, token, expiresAt) {
17+
await db.insert(tokenBlacklistTable).values({
18+
hash,
19+
token,
20+
expiresAt: moment.unix(expiresAt)
21+
});
22+
};
23+
1624
const findToken = async function (hash) {
1725
const blacklistedToken = await db.select().from(tokenBlacklistTable)
1826
.where(eq(tokenBlacklistTable.hash, hash));
@@ -22,5 +30,6 @@ const findToken = async function (hash) {
2230

2331
module.exports = {
2432
findToken,
25-
insertTokenToBlacklist
33+
insertTokenToBlacklist,
34+
insertTokenToBlacklistWithExpiresAt
2635
};

packages/models/src/token/refresh.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const findRefreshTokenUser = async function findRefreshTokenUser (token) {
2424
}
2525
});
2626

27-
return token1.user;
27+
return token1 ? token1.user : token1;
2828
};
2929

3030
module.exports = {

0 commit comments

Comments
 (0)