diff --git a/api/src/auth/auth.service.ts b/api/src/auth/auth.service.ts index 343f0efb7a59f007303db5ae8a00856422d7ab61..303b4978c8e48578e1f2d65ba77b80e1d8c7f5bf 100644 --- a/api/src/auth/auth.service.ts +++ b/api/src/auth/auth.service.ts @@ -87,6 +87,16 @@ export class AuthService { refreshToken, this.getRefreshTokenOptions(), ); + + //check if user is connected + const isConnected = await this.engineService.isSessionValid( + payload.context, + ); + + if (!isConnected) { + throw new UnauthorizedException('User need to reconnect'); + } + const user = await this.usersService.findOne(payload.context.id); const isMatchingTokens = user.refreshToken === (await this.getHash(refreshToken)); @@ -97,7 +107,8 @@ export class AuthService { } return this.login(payload.context); } catch (error) { - throw new UnauthorizedException('Invalid refresh token'); + const msg = error.message ?? 'Invalid refresh token'; + throw new UnauthorizedException(msg); } } diff --git a/api/src/engine/connectors/datashield/datashield.connector.ts b/api/src/engine/connectors/datashield/datashield.connector.ts index 0bbc8deb2e1ac039ae76f26b33ff79d982513a1a..2bbfa3e6aba6d486b33105142de72e6824064404 100644 --- a/api/src/engine/connectors/datashield/datashield.connector.ts +++ b/api/src/engine/connectors/datashield/datashield.connector.ts @@ -272,14 +272,10 @@ export default class DataShieldConnector implements Connector { ) { const path = new URL('/runAlgorithm', this.options.baseurl); - // Covariable and variable are inversed in Datashield API - const variable = - experiment.variables.length > 0 ? experiment.variables[0] : undefined; - const expToInput = { algorithm: { id: experiment.algorithm.name, - variable, + variables: experiment.variables, covariables: experiment.coVariables, }, datasets: experiment.datasets, @@ -344,6 +340,33 @@ export default class DataShieldConnector implements Connector { return [dsDomain]; } + async isSessionValid(user: User): Promise<boolean> { + const sid = user && user.extraFields && user.extraFields['sid']; + + if (!sid) return false; + + try { + const cookies = [`sid=${user.extraFields['sid']}`, `user=${user.id}`]; + const path = this.options.baseurl + 'getvars'; + + await firstValueFrom( + this.httpService.get(path, { + headers: { + cookie: cookies.join(';'), + }, + }), + ); + + return true; + } catch (err) { + DataShieldConnector.logger.verbose( + `User ${user.id} is not connected to Datashield`, + ); + DataShieldConnector.logger.debug(err); + return false; + } + } + async getActiveUser(req: Request): Promise<User> { const user = req.user as User; diff --git a/api/src/engine/engine.service.ts b/api/src/engine/engine.service.ts index fc07746b38fb38c1f6d64f843271330fdbd493b1..d0e4efd47b446a8aacc6020e46646d453dec8a44 100644 --- a/api/src/engine/engine.service.ts +++ b/api/src/engine/engine.service.ts @@ -5,6 +5,7 @@ import { Injectable, InternalServerErrorException, NotImplementedException, + UnauthorizedException, } from '@nestjs/common'; import { ConfigType } from '@nestjs/config'; import { Cache } from 'cache-manager'; @@ -79,6 +80,11 @@ export default class EngineService implements Connector { return this.connector.getConfiguration?.() ?? {}; } + isSessionValid(user: User): Promise<boolean> { + if (!this.connector.isSessionValid) throw new NotImplementedException(); + return this.connector.isSessionValid(user); + } + /** * "If the cache is enabled, try to get the value from the cache, otherwise call the function and cache * the result." @@ -162,15 +168,22 @@ export default class EngineService implements Connector { req?: Request, ): Promise<RunResult> { if (!this.connector.runExperiment) throw new NotImplementedException(); - return this.connector.runExperiment(data, req).catch((err) => ({ - results: [ - { - level: AlertLevel.ERROR, - message: `Error while running experiment, details '${err}'`, - }, - ], - status: ExperimentStatus.ERROR, - })); + return this.connector.runExperiment(data, req).catch((err) => { + if (err.status === 401 || err.response?.status === 401) { + throw new UnauthorizedException( + 'Experiment cannot be run because of a bad authentication', + ); + } + return { + results: [ + { + level: AlertLevel.ERROR, + message: `Error while running experiment, details '${err}'`, + }, + ], + status: ExperimentStatus.ERROR, + }; + }); } async listExperiments?( diff --git a/api/src/engine/interfaces/connector.interface.ts b/api/src/engine/interfaces/connector.interface.ts index d74d50a9828833b0b371a5b73cb946895b830138..22cc537e3c104a1414929443031fe7b9cdd0d2a0 100644 --- a/api/src/engine/interfaces/connector.interface.ts +++ b/api/src/engine/interfaces/connector.interface.ts @@ -22,6 +22,12 @@ export default interface Connector { */ getConfiguration?(): ConnectorConfiguration; + /** + * Tell if the session is still valid + * @param user User to check + */ + isSessionValid?(user: User): Promise<boolean>; + /** * Get the list of domains along with a list of variables * @param req - Request - this is the request object from the HTTP request.