You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
89 lines
2.7 KiB
JavaScript
89 lines
2.7 KiB
JavaScript
"use strict";
|
|
|
|
const Promise = require("bluebird");
|
|
const crypto = require("crypto");
|
|
const nanoid = require("nanoid");
|
|
const defaultValue = require("default-value");
|
|
const databaseError = require("database-error");
|
|
|
|
const errors = require("../errors");
|
|
const createDatabaseModule = require("../db-module");
|
|
|
|
function tokensMatch(a, b) {
|
|
return crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b));
|
|
}
|
|
|
|
module.exports = function ({ knex }) {
|
|
return createDatabaseModule({
|
|
create: function ({ name, deviceId, userId, token }, tx = knex) {
|
|
return Promise.try(() => {
|
|
let token_ = defaultValue(token, nanoid());
|
|
let deviceId_ = defaultValue(deviceId, nanoid());
|
|
|
|
return tx("devices").insert({
|
|
device_id: deviceId_,
|
|
user_id: userId,
|
|
name: name,
|
|
token: token_,
|
|
}).returning("*");
|
|
}).then((results) => {
|
|
return results[0];
|
|
});
|
|
},
|
|
byDeviceId: function ({ deviceId }, tx = knex) {
|
|
return Promise.try(() => {
|
|
return tx("devices").first().where({ device_id: deviceId });
|
|
}).then((result) => {
|
|
if (result != null) {
|
|
return result;
|
|
} else {
|
|
throw new errors.NotFound("The specified device ID does not exist");
|
|
}
|
|
});
|
|
},
|
|
byDeviceIdAndToken: function ({ deviceId, token }, _tx = knex) {
|
|
/* NOTE: This is intentionally a separate method as opposed to conditional logic in byDeviceId, to prevent cases where a bug results in a user-specified token not being passed in - which would fail unsafely by allowing any token, if conditional logic were used. */
|
|
return Promise.try(() => {
|
|
if (token != null) {
|
|
return this.byDeviceId({ deviceId });
|
|
} else {
|
|
throw new errors.MissingAccessToken("No access token was specified");
|
|
}
|
|
}).then((device) => {
|
|
if (tokensMatch(token, device.token)) {
|
|
return device;
|
|
} else {
|
|
throw new errors.InvalidAccessToken("Specified token was invalid");
|
|
}
|
|
});
|
|
},
|
|
updateToken: function ({ userId, deviceId, token }, tx = knex) {
|
|
return Promise.try(() => {
|
|
return tx("devices")
|
|
.update({ token: token })
|
|
.where({
|
|
device_id: deviceId,
|
|
user_id: userId
|
|
})
|
|
.returning("*");
|
|
}).then((results) => {
|
|
if (results.length > 0) {
|
|
return results[0];
|
|
} else {
|
|
throw new errors.NotFound("Specified Device ID does not exist");
|
|
}
|
|
});
|
|
},
|
|
createDeviceSession: function ({ name, deviceId, userId }, _tx = knex) {
|
|
return Promise.try(() => {
|
|
let token = nanoid();
|
|
|
|
return Promise.try(() => {
|
|
return this.create({ name, deviceId, userId, token });
|
|
}).catch({ name: "UniqueConstraintViolationError", table: "devices", column: "device_id" }, (_error) => {
|
|
return this.updateToken({ userId, deviceId, token });
|
|
});
|
|
});
|
|
}
|
|
});
|
|
}; |