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.

91 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 });
}).then((device) => {
/* FIXME */
});
});
}
});
};