From 0098e64c34284db1e422d754427da124a79b062d Mon Sep 17 00:00:00 2001 From: Anton SHEPILOV Date: Wed, 29 May 2024 22:27:03 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9BFixed=20bug=20with=20ability=20to?= =?UTF-8?q?=20log=20in=20with=20revoked=20session?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/services/console/clients/remote.ts | 10 ++++-- .../src/services/console/entities/session.ts | 5 ++- .../backend/node/test/e2e/common/user-api.ts | 9 ++---- .../e2e/console/backchannel-logout.spec.ts | 32 ++++++++++++++++++- 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/tdrive/backend/node/src/services/console/clients/remote.ts b/tdrive/backend/node/src/services/console/clients/remote.ts index 979c74f79..72af137fc 100644 --- a/tdrive/backend/node/src/services/console/clients/remote.ts +++ b/tdrive/backend/node/src/services/console/clients/remote.ts @@ -265,6 +265,9 @@ export class ConsoleRemoteClient implements ConsoleServiceClient { sid: sessionInfo.sid, }); if (existingSession) { + if (existingSession.revoked_at) { + throw CrudException.unauthorized(`Session ${sessionInfo.sid} expired`); + } return existingSession.sid; } else { const sessionBody = new Session(); @@ -320,7 +323,8 @@ export class ConsoleRemoteClient implements ConsoleServiceClient { const sessionRepository = gr.services.console.getSessionRepo(); const session = await sessionRepository.findOne({ sid: payload.claims.sid }); if (session) { - await sessionRepository.remove(session); + session.revoked_at = new Date().getTime(); + await sessionRepository.save(session); } } @@ -332,7 +336,9 @@ export class ConsoleRemoteClient implements ConsoleServiceClient { }); if (!session) { // fail for not matching session id - throw new Error("Invalid session id"); + throw new Error(`Session ${sid} not found`); + } else if (session.revoked_at > 0) { + throw new Error(`Session ${sid} revoked`); } } else { // fail for missing session id diff --git a/tdrive/backend/node/src/services/console/entities/session.ts b/tdrive/backend/node/src/services/console/entities/session.ts index 43725edeb..1ae4bc73b 100644 --- a/tdrive/backend/node/src/services/console/entities/session.ts +++ b/tdrive/backend/node/src/services/console/entities/session.ts @@ -5,7 +5,7 @@ export const TYPE = "session"; @Entity(TYPE, { primaryKey: [["company_id"], "sid"], - globalIndexes: [["sid"]], + globalIndexes: [["sid"], ["revoked_at"]], type: TYPE, }) export default class Session { @@ -17,6 +17,9 @@ export default class Session { @Column("sid", "string") sid: string; + + @Column("revoked_at", "number") + revoked_at: number; } export type UserSessionPrimaryKey = Pick; diff --git a/tdrive/backend/node/test/e2e/common/user-api.ts b/tdrive/backend/node/test/e2e/common/user-api.ts index 07f56dfd7..f369ba916 100644 --- a/tdrive/backend/node/test/e2e/common/user-api.ts +++ b/tdrive/backend/node/test/e2e/common/user-api.ts @@ -78,7 +78,7 @@ export default class UserApi { this.jwt = await this.doLogin(); } - private async doLogin() { + public async doLogin() { const loginResponse = await this.login(); expect(loginResponse).toBeDefined(); @@ -133,13 +133,10 @@ export default class UserApi { verifierMock.mockImplementation(() => { return Promise.resolve(payload); // Return the predefined payload }); - const logoutToken = "logout_token_rsa256"; - const response = await this.api.post("/internal/services/console/v1/backchannel_logout", { - logout_token: logoutToken, + return await this.api.post("/internal/services/console/v1/backchannel_logout", { + logout_token: "logout_token_rsa256", }); - - return response; } diff --git a/tdrive/backend/node/test/e2e/console/backchannel-logout.spec.ts b/tdrive/backend/node/test/e2e/console/backchannel-logout.spec.ts index 876c5fcfd..3d7d53d09 100644 --- a/tdrive/backend/node/test/e2e/console/backchannel-logout.spec.ts +++ b/tdrive/backend/node/test/e2e/console/backchannel-logout.spec.ts @@ -61,7 +61,8 @@ describe("The /backchannel_logout API", () => { // Verify the session is removed from the database const deletedSession = await currentUser.dbService.getSessionById(currentUser.session); - expect(deletedSession).toBeNull(); + expect(deletedSession).not.toBeNull(); + expect(deletedSession.revoked_at).toBeGreaterThan(0); }); it("should create a session on login", async () => { @@ -82,6 +83,35 @@ describe("The /backchannel_logout API", () => { expect(response.statusCode).toBe(401); }); + it("should receive 401 after logout and try to login one more time with the same token", async () => { + //when + await currentUser.logout(); + + //then + const response = await currentUser.login(currentUser.session); + expect(response.statusCode).toBe(401); + }); + + it("should receive 401 after logout successfully after logout", async () => { + //given + const myDriveId = "user_" + currentUser.user.id; + let response = await currentUser.getDocument(myDriveId); + expect(response.statusCode).toBe(200); + + //when + await currentUser.logout(); + + //then + response = await currentUser.login(currentUser.session); + expect(response.statusCode).toBe(401); + + currentUser.jwt = await currentUser.doLogin(); + + + response = await currentUser.getDocument(myDriveId); + expect(response.statusCode).toBe(200); + }); + it("should be able to log-in several times by having multiple sessions", async () => { // Perform a second login const newUserSession = await UserApi.getInstance(platform);