Track recently active and signin times (#663)
* Track recently active and signin times * Trust proxy headers in production
This commit is contained in:
@ -86,6 +86,9 @@ router.get('google.callback', async ctx => {
|
||||
await team.createFirstCollection(user.id);
|
||||
}
|
||||
|
||||
// not awaiting the promise here so that the request is not blocked
|
||||
user.updateSignedIn(ctx.request.ip);
|
||||
|
||||
ctx.cookies.set('lastSignedIn', 'google', {
|
||||
httpOnly: false,
|
||||
expires: new Date('2100'),
|
||||
|
@ -62,6 +62,9 @@ router.get('slack.callback', async ctx => {
|
||||
await team.createFirstCollection(user.id);
|
||||
}
|
||||
|
||||
// not awaiting the promise here so that the request is not blocked
|
||||
user.updateSignedIn(ctx.request.ip);
|
||||
|
||||
ctx.cookies.set('lastSignedIn', 'slack', {
|
||||
httpOnly: false,
|
||||
expires: new Date('2100'),
|
||||
|
@ -70,6 +70,10 @@ if (process.env.NODE_ENV === 'development') {
|
||||
|
||||
app.use(mount('/emails', emails));
|
||||
} else if (process.env.NODE_ENV === 'production') {
|
||||
// trust header fields set by our proxy. eg X-Forwarded-For
|
||||
app.proxy = true;
|
||||
|
||||
// catch errors in one place, automatically set status and response headers
|
||||
onerror(app);
|
||||
|
||||
if (process.env.BUGSNAG_KEY) {
|
||||
|
@ -79,6 +79,9 @@ export default function auth(options?: { required?: boolean } = {}) {
|
||||
throw new UserSuspendedError({ adminEmail: suspendingAdmin.email });
|
||||
}
|
||||
|
||||
// not awaiting the promise here so that the request is not blocked
|
||||
user.updateActiveAt(ctx.request.ip);
|
||||
|
||||
ctx.state.token = token;
|
||||
ctx.state.user = user;
|
||||
// $FlowFixMe
|
||||
|
26
server/migrations/20180604182823-user-tracking.js
Normal file
26
server/migrations/20180604182823-user-tracking.js
Normal file
@ -0,0 +1,26 @@
|
||||
module.exports = {
|
||||
up: async (queryInterface, Sequelize) => {
|
||||
await queryInterface.addColumn('users', 'lastActiveAt', {
|
||||
type: Sequelize.DATE,
|
||||
allowNull: true
|
||||
});
|
||||
await queryInterface.addColumn('users', 'lastActiveIp', {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: true
|
||||
});
|
||||
await queryInterface.addColumn('users', 'lastSignedInAt', {
|
||||
type: Sequelize.DATE,
|
||||
allowNull: true
|
||||
});
|
||||
await queryInterface.addColumn('users', 'lastSignedInIp', {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: true
|
||||
});
|
||||
},
|
||||
down: async (queryInterface, Sequelize) => {
|
||||
await queryInterface.removeColumn('users', 'lastActiveAt');
|
||||
await queryInterface.removeColumn('users', 'lastActiveIp');
|
||||
await queryInterface.removeColumn('users', 'lastSignedInAt');
|
||||
await queryInterface.removeColumn('users', 'lastSignedInIp');
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ import crypto from 'crypto';
|
||||
import bcrypt from 'bcrypt';
|
||||
import uuid from 'uuid';
|
||||
import JWT from 'jsonwebtoken';
|
||||
import subMinutes from 'date-fns/sub_minutes';
|
||||
import { DataTypes, sequelize, encryptedFields } from '../sequelize';
|
||||
import { publicS3Endpoint, uploadToS3FromUrl } from '../utils/s3';
|
||||
import { sendEmail } from '../mailer';
|
||||
@ -28,6 +29,10 @@ const User = sequelize.define(
|
||||
serviceId: { type: DataTypes.STRING, allowNull: true, unique: true },
|
||||
slackData: DataTypes.JSONB,
|
||||
jwtSecret: encryptedFields.vault('jwtSecret'),
|
||||
lastActiveAt: DataTypes.DATE,
|
||||
lastActiveIp: DataTypes.STRING,
|
||||
lastSignedInAt: DataTypes.DATE,
|
||||
lastSignedInIp: DataTypes.STRING,
|
||||
suspendedAt: DataTypes.DATE,
|
||||
suspendedById: DataTypes.UUID,
|
||||
},
|
||||
@ -53,6 +58,24 @@ User.associate = models => {
|
||||
};
|
||||
|
||||
// Instance methods
|
||||
User.prototype.updateActiveAt = function(ip) {
|
||||
const fiveMinutesAgo = subMinutes(new Date(), 5);
|
||||
|
||||
// ensure this is updated only every few minutes otherwise
|
||||
// we'll be constantly writing to the DB as API requests happen
|
||||
if (this.lastActiveAt < fiveMinutesAgo) {
|
||||
this.lastActiveAt = new Date();
|
||||
this.lastActiveIp = ip;
|
||||
return this.save({ hooks: false });
|
||||
}
|
||||
};
|
||||
|
||||
User.prototype.updateSignedIn = function(ip) {
|
||||
this.lastSignedInAt = new Date();
|
||||
this.lastSignedInIp = ip;
|
||||
return this.save({ hooks: false });
|
||||
};
|
||||
|
||||
User.prototype.getJwtToken = function() {
|
||||
return JWT.sign({ id: this.id }, this.jwtSecret);
|
||||
};
|
||||
|
Reference in New Issue
Block a user