From 57c3d949cf80b97785bc58289949d7170894d758 Mon Sep 17 00:00:00 2001 From: Ivy Date: Thu, 3 Oct 2019 13:08:06 -0700 Subject: [PATCH 1/2] fixes#1305 lastActiveAt --- backend/src/jwt/decode.js | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/jwt/decode.js b/backend/src/jwt/decode.js index d022f3512..842f8f537 100644 --- a/backend/src/jwt/decode.js +++ b/backend/src/jwt/decode.js @@ -14,6 +14,7 @@ export default async (driver, authorizationHeader) => { const session = driver.session() const query = ` MATCH (user:User {id: $id, deleted: false, disabled: false }) + SET user.lastActiveAt = toString(datetime()) RETURN user {.id, .slug, .name, .avatar, .email, .role, .disabled, .actorId} LIMIT 1 ` From 78bf07649856b2be043c09dc920568b3aa2df7ed Mon Sep 17 00:00:00 2001 From: roschaefer Date: Fri, 4 Oct 2019 01:39:18 +0200 Subject: [PATCH 2/2] test: add tests for #1809 --- backend/src/jwt/decode.spec.js | 31 ++++++++++++++++++++++++++++++- backend/src/models/User.js | 1 + 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/backend/src/jwt/decode.spec.js b/backend/src/jwt/decode.spec.js index df6914f25..9ea858304 100644 --- a/backend/src/jwt/decode.spec.js +++ b/backend/src/jwt/decode.spec.js @@ -1,9 +1,10 @@ import Factory from '../seed/factories/index' -import { getDriver } from '../bootstrap/neo4j' +import { getDriver, neode as getNeode } from '../bootstrap/neo4j' import decode from './decode' const factory = Factory() const driver = getDriver() +const neode = getNeode() // here is the decoded JWT token: // { @@ -85,6 +86,33 @@ describe('decode', () => { }) }) + it('sets `lastActiveAt`', async () => { + let user = await neode.first('User', { id: 'u3' }) + await expect(user.toJson()).resolves.not.toHaveProperty('lastActiveAt') + await decode(driver, authorizationHeader) + user = await neode.first('User', { id: 'u3' }) + await expect(user.toJson()).resolves.toMatchObject({ + lastActiveAt: expect.any(String), + }) + }) + + it('updates `lastActiveAt` for every authenticated request', async () => { + let user = await neode.first('User', { id: 'u3' }) + await user.update({ + updatedAt: new Date().toISOString(), + lastActiveAt: '2019-10-03T23:33:08.598Z', + }) + await expect(user.toJson()).resolves.toMatchObject({ + lastActiveAt: '2019-10-03T23:33:08.598Z', + }) + await decode(driver, authorizationHeader) + user = await neode.first('User', { id: 'u3' }) + await expect(user.toJson()).resolves.toMatchObject({ + // should be a different time by now ;) + lastActiveAt: expect.not.stringContaining('2019-10-03T23:33'), + }) + }) + describe('but user is deleted', () => { beforeEach(async () => { await user.update({ updatedAt: new Date().toISOString(), deleted: true }) @@ -92,6 +120,7 @@ describe('decode', () => { it('returns null', returnsNull) }) + describe('but user is disabled', () => { beforeEach(async () => { await user.update({ updatedAt: new Date().toISOString(), disabled: true }) diff --git a/backend/src/models/User.js b/backend/src/models/User.js index 4bab080ca..6448d7450 100644 --- a/backend/src/models/User.js +++ b/backend/src/models/User.js @@ -49,6 +49,7 @@ module.exports = { direction: 'in', }, invitedBy: { type: 'relationship', relationship: 'INVITED', target: 'User', direction: 'in' }, + lastActiveAt: { type: 'string', isoDate: true }, createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() }, updatedAt: { type: 'string',