diff --git a/.env b/.env new file mode 100644 index 0000000..bd6c7ec --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +SCICAT_API_BASE_PATH= +SCICAT_API_ACCESS_TOKEN= diff --git a/config/config.json b/config/config.json index eb9ad3c..a685353 100644 --- a/config/config.json +++ b/config/config.json @@ -13,5 +13,7 @@ "graylogEnabled": false, "graylogServer": "it-graylog.esss.lu.se", "graylogPort": 12201, - "environment": "development" + "environment": "development", + "basePath": "", + "accessToken": "" } diff --git a/package-lock.json b/package-lock.json index c7533e1..42f425e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,8 @@ "name": "zip-service", "version": "0.0.0", "dependencies": { + "@scicatproject/scicat-sdk-ts": "^4.8.0", + "@scicatproject/scicat-ts-fetch-test": "^2.0.0", "@types/archiver": "^5.3.1", "@types/cookie-parser": "^1.4.3", "@types/cors": "^2.8.12", @@ -23,6 +25,7 @@ "cookie-parser": "~1.4.6", "cors": "^2.8.5", "debug": "~2.6.9", + "dotenv": "^16.4.5", "ejs": "^3.1.8", "express": "~4.19.2", "express-fileupload": "^1.4.0", @@ -43,6 +46,28 @@ "eslint": "^7.22.0" } }, + "node_modules/@angular/core": { + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-16.2.12.tgz", + "integrity": "sha512-GLLlDeke/NjroaLYOks0uyzFVo6HyLl7VOm0K1QpLXnYvW63W9Ql/T3yguRZa7tRkOAeFZ3jw+1wnBD4O8MoUA==", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0" + }, + "peerDependencies": { + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.13.0" + } + }, + "node_modules/@angular/core/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "peer": true + }, "node_modules/@babel/helper-validator-identifier": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", @@ -163,6 +188,28 @@ "node": ">= 8" } }, + "node_modules/@scicatproject/scicat-sdk-ts": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@scicatproject/scicat-sdk-ts/-/scicat-sdk-ts-4.8.0.tgz", + "integrity": "sha512-BgkdUyfTTi1XfcuM/ItjHBebl/UUr1H7dwcqA/CKUQPy9ADLbZL5ubB3ZZceGp9Ahh3g0i/4rjifn1im6guIAA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/core": "^16.2.12", + "rxjs": "^7.4.0" + } + }, + "node_modules/@scicatproject/scicat-sdk-ts/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/@scicatproject/scicat-ts-fetch-test": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@scicatproject/scicat-ts-fetch-test/-/scicat-ts-fetch-test-2.0.0.tgz", + "integrity": "sha512-1TmuU4wda3RnZ1rkaiQ1KSJ9CXKvKZFPGFDj/2MSfZv/bOtBOAzDtUNLzazcapackwKTZgbJRUoaLmUFQaOrHQ==" + }, "node_modules/@types/archiver": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@types/archiver/-/archiver-5.3.1.tgz", @@ -1199,6 +1246,17 @@ "node": ">=6.0.0" } }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -3154,6 +3212,21 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "peer": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/rxjs/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "peer": true + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -3664,9 +3737,41 @@ "engines": { "node": ">= 6" } + }, + "node_modules/zone.js": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.13.3.tgz", + "integrity": "sha512-MKPbmZie6fASC/ps4dkmIhaT5eonHkEt6eAy80K42tAm0G2W+AahLJjbfi6X9NPdciOE9GRFTTM8u2IiF6O3ww==", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + } + }, + "node_modules/zone.js/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "peer": true } }, "dependencies": { + "@angular/core": { + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-16.2.12.tgz", + "integrity": "sha512-GLLlDeke/NjroaLYOks0uyzFVo6HyLl7VOm0K1QpLXnYvW63W9Ql/T3yguRZa7tRkOAeFZ3jw+1wnBD4O8MoUA==", + "peer": true, + "requires": { + "tslib": "^2.3.0" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "peer": true + } + } + }, "@babel/helper-validator-identifier": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", @@ -3765,6 +3870,26 @@ "fastq": "^1.6.0" } }, + "@scicatproject/scicat-sdk-ts": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@scicatproject/scicat-sdk-ts/-/scicat-sdk-ts-4.8.0.tgz", + "integrity": "sha512-BgkdUyfTTi1XfcuM/ItjHBebl/UUr1H7dwcqA/CKUQPy9ADLbZL5ubB3ZZceGp9Ahh3g0i/4rjifn1im6guIAA==", + "requires": { + "tslib": "^2.3.0" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + } + } + }, + "@scicatproject/scicat-ts-fetch-test": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@scicatproject/scicat-ts-fetch-test/-/scicat-ts-fetch-test-2.0.0.tgz", + "integrity": "sha512-1TmuU4wda3RnZ1rkaiQ1KSJ9CXKvKZFPGFDj/2MSfZv/bOtBOAzDtUNLzazcapackwKTZgbJRUoaLmUFQaOrHQ==" + }, "@types/archiver": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@types/archiver/-/archiver-5.3.1.tgz", @@ -4624,6 +4749,11 @@ "esutils": "^2.0.2" } }, + "dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==" + }, "ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -6117,6 +6247,23 @@ "queue-microtask": "^1.2.2" } }, + "rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "peer": true, + "requires": { + "tslib": "^2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "peer": true + } + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -6520,6 +6667,23 @@ } } } + }, + "zone.js": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.13.3.tgz", + "integrity": "sha512-MKPbmZie6fASC/ps4dkmIhaT5eonHkEt6eAy80K42tAm0G2W+AahLJjbfi6X9NPdciOE9GRFTTM8u2IiF6O3ww==", + "peer": true, + "requires": { + "tslib": "^2.3.0" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "peer": true + } + } } } } diff --git a/package.json b/package.json index f9b77b4..8db5f82 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "lint": "eslint --fix ." }, "dependencies": { + "@scicatproject/scicat-ts-fetch-test": "^2.0.0", "@types/archiver": "^5.3.1", "@types/cookie-parser": "^1.4.3", "@types/cors": "^2.8.12", diff --git a/src/auth.ts b/src/auth.ts index e3540a3..809fa54 100644 --- a/src/auth.ts +++ b/src/auth.ts @@ -1,15 +1,21 @@ import express from "express"; import { config } from "./common/config"; -import jwtLib from "jsonwebtoken"; import * as fs from "fs"; +import jwtLib from "jsonwebtoken"; + import { logger } from "@user-office-software/duo-logger"; +import { scicatDataSetAPI } from "./common/scicatAPI" -export const hasFileAccess = ( +export const hasFileAccess = async ( req: express.Request, directory: string, - fileNames: string[] -): Global.AuthResponse => { - const { jwtSecret, facility } = config; + fileNames: string[], + dataset: string +): Promise => { + + const { jwtSecret } = config; + const dataSetAPI = scicatDataSetAPI(); + if (!jwtSecret) { return { hasAccess: false, @@ -41,7 +47,10 @@ export const hasFileAccess = ( httpMethod: req.method, directory, fileNames, + dataset }; + + if (!authRequest.directory) { return { hasAccess: false, @@ -79,32 +88,29 @@ export const hasFileAccess = ( fileNames: [], }; } - // Evaluate access rights based on institution specific logic - switch (facility) { - case "maxiv": { - return authMAXIV(authRequest); - } - default: - return { - hasAccess: true, - statusCode: 200, - directory: authRequest.directory, - fileNames: authRequest.fileNames, - }; - } -}; -const authMAXIV = (authRequest: Global.AuthRequest): Global.AuthResponse => { - const valid = - authRequest.jwt.groups.filter( - (group) => group.trim() && authRequest.directory.indexOf(group) > -1 - ).length > 0; - return { - hasAccess: valid, - statusCode: valid ? 200 : 403, - error: valid ? "" : "You do not have access to this resource", - directory: valid ? authRequest.directory : undefined, - fileNames: valid ? authRequest.fileNames : [], - }; -}; + +const isPublic = value.isPublished; +const hasAccessGroup = value.accessGroups.some(item => new Set(authRequest.jwt.groups).has(item)); +const hasOwnerGroup = authRequest.jwt.groups.includes(value.ownerGroup); +if (isPublic || hasAccessGroup || hasOwnerGroup) { + return true; +} + + return false; + + } + ).catch((e) => { + + return false; + }); + + return { + hasAccess: valid, + statusCode: valid ? 200 : 403, + error: valid ? "" : "You do not have access to this resource", + directory: valid ? authRequest.directory : undefined, + fileNames: valid ? authRequest.fileNames : [], + }; +} diff --git a/src/common/scicatAPI.ts b/src/common/scicatAPI.ts new file mode 100644 index 0000000..e1777d1 --- /dev/null +++ b/src/common/scicatAPI.ts @@ -0,0 +1,24 @@ +import { Configuration, DatasetsApi } from "@scicatproject/scicat-ts-fetch-test"; +import { config } from "./config"; + +let datasetsApiInstance: DatasetsApi | null = null; + +export function scicatDataSetAPI(): DatasetsApi { + const { basePath, accessToken } = config; + + if (!datasetsApiInstance) { + + if (!basePath || !accessToken) { + throw new Error("SciCat API configuration is missing: Check SCICAT_API_BASE_PATH and SCICAT_API_ACCESS_TOKEN."); + } + + const apiConfig = new Configuration({ + basePath , + accessToken, + }); + + datasetsApiInstance = new DatasetsApi(apiConfig); + } + + return datasetsApiInstance; +} diff --git a/src/routes/index.ts b/src/routes/index.ts index 7325613..16268a8 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -6,6 +6,7 @@ router.get("/", function (req, res) { res.render("index", { jwt: config.testData.jwt || "", directory: config.testData.directory || "", + dataset: "", file0: config.testData?.files[0] || "", file1: config.testData?.files[1] || "", file2: config.testData?.files[2] || "", diff --git a/src/routes/zip.ts b/src/routes/zip.ts index 93259ee..93ce66c 100644 --- a/src/routes/zip.ts +++ b/src/routes/zip.ts @@ -12,16 +12,17 @@ export const router = express.Router(); /** * Request zipping of files. Require directory:string and files:string[] in the request body */ -router.post("/", (req, res) => { +router.post("/", async (req, res) => { logger.logInfo("Request has been submitted", { directory: req.body.directory, fileNames: req.body.files, }); - const { hasAccess, statusCode, error, directory, fileNames } = hasFileAccess( + const { hasAccess, statusCode, error, directory, fileNames } = await hasFileAccess( req, req.body.directory, - req.body.files + req.body.files, + req.body.dataset ); if (!hasAccess) { diff --git a/src/routes/zip_in_place.ts b/src/routes/zip_in_place.ts index 1086e26..10bf565 100644 --- a/src/routes/zip_in_place.ts +++ b/src/routes/zip_in_place.ts @@ -9,11 +9,12 @@ import { logger } from "@user-office-software/duo-logger"; export const router = express.Router(); /* POST zip */ -router.post("/", function (req: express.Request, res: express.Response) { - const { hasAccess, statusCode, error, directory, fileNames } = hasFileAccess( +router.post("/", async function (req: express.Request, res: express.Response) { + const { hasAccess, statusCode, error, directory, fileNames } = await hasFileAccess( req, req.body.directory, - req.body.files + req.body.files, + req.body.dataset ); const readOpts = { highWaterMark: Math.pow(2, 20) }; diff --git a/src/types/global.d.ts b/src/types/global.d.ts index 78e4a52..d470047 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -27,6 +27,7 @@ declare global { httpMethod: string; directory: string; fileNames: string[]; + dataset: string; } interface AuthResponse { hasAccess: boolean; diff --git a/src/views/index.ejs b/src/views/index.ejs index 292c29c..fc31ff6 100644 --- a/src/views/index.ejs +++ b/src/views/index.ejs @@ -51,6 +51,15 @@ value="<%= directory %>" /> +
+
Dataset PID
+ +
File 1