From 68a56d8272071214e7e2c03e676de093c301ec51 Mon Sep 17 00:00:00 2001 From: jumpaolo Date: Fri, 28 Jul 2023 11:49:08 +0200 Subject: [PATCH 1/5] Aggiornate dipendenze packages.json del client validator --- spid-validator/client/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spid-validator/client/package.json b/spid-validator/client/package.json index 7454df0..659bcc3 100644 --- a/spid-validator/client/package.json +++ b/spid-validator/client/package.json @@ -19,9 +19,9 @@ "file-loader": "1.1.6", "html-loader": "0.5.4", "html-webpack-plugin": "2.30.1", - "node-sass": "^4.13.1", + "sass": "1.35.2", "rimraf": "2.6.2", - "sass-loader": "6.0.6", + "sass-loader": "^7.3.1", "source-list-map": "2.0.0", "style-loader": "0.19.1", "uglify-js": "3.3.7", From 81f080dcde24736f04f913e02ae74c66ea2b00d9 Mon Sep 17 00:00:00 2001 From: damikael Date: Fri, 22 Sep 2023 00:29:33 +0200 Subject: [PATCH 2/5] feat: upload zip --- .../client/src/components/AceEditor/index.js | 25 +- .../client/src/components/AceEditor/view.js | 4 +- .../client/src/components/Sidebar/_nav.js | 7 +- .../client/src/containers/Main/Main.js | 4 +- spid-validator/client/src/index.js | 1 + spid-validator/client/src/routes.js | 3 +- spid-validator/client/src/services.js | 16 ++ .../src/views/MetadataSpDownload/view.js | 2 +- .../MetadataSpUploadZip.js | 79 +++++++ .../views/MetadataSpUploadZip/package.json | 6 + .../src/views/MetadataSpUploadZip/style.css | 15 ++ .../src/views/MetadataSpUploadZip/view.js | 47 ++++ spid-validator/server/.gitignore | 2 + spid-validator/server/api/metadata-sp.js | 217 +++++++++++++++--- spid-validator/server/lib/utils.js | 11 + .../server/npm-shrinkwrap.json-original | 7 + spid-validator/server/package.json | 5 +- 17 files changed, 409 insertions(+), 42 deletions(-) create mode 100644 spid-validator/client/src/views/MetadataSpUploadZip/MetadataSpUploadZip.js create mode 100644 spid-validator/client/src/views/MetadataSpUploadZip/package.json create mode 100644 spid-validator/client/src/views/MetadataSpUploadZip/style.css create mode 100644 spid-validator/client/src/views/MetadataSpUploadZip/view.js create mode 100644 spid-validator/server/npm-shrinkwrap.json-original diff --git a/spid-validator/client/src/components/AceEditor/index.js b/spid-validator/client/src/components/AceEditor/index.js index 59dbbe5..9bdfbd3 100644 --- a/spid-validator/client/src/components/AceEditor/index.js +++ b/spid-validator/client/src/components/AceEditor/index.js @@ -7,13 +7,32 @@ class AceEditor extends Component { constructor(props) { super(props); - this.state = {code: format((props.code!=null)? props.code:"")}; + this.state = { + mode: 'xml', + code: "" + }; } static getDerivedStateFromProps(props, state) { - Utility.log("State", props.code) + + let mode = props.mode; + let code = props.code; + + switch(props.mode) { + case 'json': + code = JSON.stringify(props.code, null, 4); + break; + + case 'xml': + default: + mode = 'xml'; + code = format((props.code!=null)? props.code : ""); + break; + } + return { - code: format((props.code!=null)? props.code:"") + code: code, + mode: mode } } diff --git a/spid-validator/client/src/components/AceEditor/view.js b/spid-validator/client/src/components/AceEditor/view.js index ef9a0c1..2f78349 100644 --- a/spid-validator/client/src/components/AceEditor/view.js +++ b/spid-validator/client/src/components/AceEditor/view.js @@ -1,6 +1,7 @@ import React from 'react'; import AceEditor from 'react-ace'; import 'brace/mode/xml'; +import 'brace/mode/json'; import 'brace/theme/cobalt'; import "./style.css"; @@ -8,7 +9,7 @@ function view(me) { return( diff --git a/spid-validator/client/src/components/Sidebar/_nav.js b/spid-validator/client/src/components/Sidebar/_nav.js index 0b0067d..d199d16 100644 --- a/spid-validator/client/src/components/Sidebar/_nav.js +++ b/spid-validator/client/src/components/Sidebar/_nav.js @@ -7,10 +7,15 @@ export default { sessionRequired: false, children: [ { - name: 'Download', + name: 'Download from URL', url: '/metadata-sp-download', sessionRequired: false, }, + { + name: 'Upload ZIP', + url: '/metadata-sp-upload-zip', + sessionRequired: false, + }, { name: 'Check XSD', url: '/metadata-sp-check-xsd', diff --git a/spid-validator/client/src/containers/Main/Main.js b/spid-validator/client/src/containers/Main/Main.js index 4723259..4925af2 100644 --- a/spid-validator/client/src/containers/Main/Main.js +++ b/spid-validator/client/src/containers/Main/Main.js @@ -9,6 +9,7 @@ import Breadcrumb from '../../components/Breadcrumb/'; import Aside from '../../components/Aside/'; import Footer from '../../components/Footer/'; import MetadataSpDownload from '../../views/MetadataSpDownload/'; +import MetadataSpUploadZip from '../../views/MetadataSpUploadZip/'; import MetadataSpCheck from '../../views/MetadataSpCheck/'; import Request from '../../views/Request/'; import RequestCheck from '../../views/RequestCheck/'; @@ -158,7 +159,8 @@ class Main extends Component { - + + } /> } /> } /> diff --git a/spid-validator/client/src/index.js b/spid-validator/client/src/index.js index a0838c4..8ff9747 100644 --- a/spid-validator/client/src/index.js +++ b/spid-validator/client/src/index.js @@ -25,6 +25,7 @@ ReactDOM.render(( + diff --git a/spid-validator/client/src/routes.js b/spid-validator/client/src/routes.js index 2bcb581..762a692 100644 --- a/spid-validator/client/src/routes.js +++ b/spid-validator/client/src/routes.js @@ -1,6 +1,7 @@ const routes = { '/': 'Home', - '/metadata-sp-download': 'Metadata Service Provider / Download', + '/metadata-sp-download': 'Metadata Service Provider / Download from URL', + '/metadata-sp-upload-zip': 'Metadata Service Provider / Upload ZIP', '/metadata-sp-checl-xsd': 'Metadata Service Provider / Check XSD', '/metadata-sp-check-strict': 'Metadata Service Provider / Check Strict', '/metadata-sp-check-certs': 'Metadata Service Provider / Check Certificates', diff --git a/spid-validator/client/src/services.js b/spid-validator/client/src/services.js index e451cdf..0812acd 100644 --- a/spid-validator/client/src/services.js +++ b/spid-validator/client/src/services.js @@ -140,6 +140,22 @@ class MainService { }); } + uploadFile(file, callback_progress, callback_response, callback_error) { + Utility.log("POST: /api/metadata-sp/upload/zip"); + const formData = new FormData(); + formData.append('file', file); + axios.post(' /api/metadata-sp/upload/zip?apikey=' + Utility.getApikey(), formData, { + headers: { 'Content-Type': 'multipart/form-data' }, + onUploadProgress: (progressEvent)=>callback_progress(progressEvent) + }) + .then(function(response) { + callback_response(response.data); + }) + .catch(function(error) { + callback_error((error.response!=null) ? error.response.data : "Service not available"); + }); + } + getLastCheckMetadataSp(test, callback_response, callback_error) { Utility.log("GET /api/metadata-sp/lastcheck/" + test); axios.get('/api/metadata-sp/lastcheck/' + test + '?apikey=' + Utility.getApikey(), {timeout: 900000}) diff --git a/spid-validator/client/src/views/MetadataSpDownload/view.js b/spid-validator/client/src/views/MetadataSpDownload/view.js index 1307754..5597237 100644 --- a/spid-validator/client/src/views/MetadataSpDownload/view.js +++ b/spid-validator/client/src/views/MetadataSpDownload/view.js @@ -12,7 +12,7 @@ function view(me) {
Metadata URL
-
+
{ + this.setState({progress: (progress.loaded*100)/progress.total}); + }, + (uploaded)=> { + this.setState({ + loading: false, + loaded: false, + progress: 0, + result: uploaded + }); + + Utility.blockUI(false); + }, + (error)=> { + Utility.showModal({ + title: "Errore", + body: error, + isOpen: true + }); + this.setState({ + loading: false, + loaded: false + }); + + Utility.blockUI(false); + } + ); + } + +} + +export default MetadataSpUploadZip; diff --git a/spid-validator/client/src/views/MetadataSpUploadZip/package.json b/spid-validator/client/src/views/MetadataSpUploadZip/package.json new file mode 100644 index 0000000..3f82771 --- /dev/null +++ b/spid-validator/client/src/views/MetadataSpUploadZip/package.json @@ -0,0 +1,6 @@ +{ + "name": "MetadataSpUploadZip", + "version": "0.0.0", + "private": true, + "main": "./MetadataSpUploadZip.js" +} diff --git a/spid-validator/client/src/views/MetadataSpUploadZip/style.css b/spid-validator/client/src/views/MetadataSpUploadZip/style.css new file mode 100644 index 0000000..669274a --- /dev/null +++ b/spid-validator/client/src/views/MetadataSpUploadZip/style.css @@ -0,0 +1,15 @@ +.title { + margin-bottom: 30px!important; +} + +input.metadata { + +} + +.code { + margin-top: 20px; +} + +button { + margin: 0 5px!important; +} diff --git a/spid-validator/client/src/views/MetadataSpUploadZip/view.js b/spid-validator/client/src/views/MetadataSpUploadZip/view.js new file mode 100644 index 0000000..6cef0b3 --- /dev/null +++ b/spid-validator/client/src/views/MetadataSpUploadZip/view.js @@ -0,0 +1,47 @@ +import React from 'react'; +import { UncontrolledTooltip } from 'reactstrap'; +import BlockUi from 'react-block-ui'; +import AceEditor from '../../components/AceEditor/'; +import "./style.css"; + +function view(me) { + return ( +
+

Metadata Service Provider

+
+
+ Metadata ZIP +
+
+ {!me.state.loading && +
+ + +
+ } + {me.state.loading && +
+ Uploading file... {me.state.progress}% +
+ } +
+
+ {me.state.result!="" && +
+
+ +
+
+ } +
+ ); +} + +export default view; diff --git a/spid-validator/server/.gitignore b/spid-validator/server/.gitignore index c1b03a1..144570b 100644 --- a/spid-validator/server/.gitignore +++ b/spid-validator/server/.gitignore @@ -16,3 +16,5 @@ npm-debug.log* yarn-debug.log* yarn-error.log* + +npm-shrinkwrap.json diff --git a/spid-validator/server/api/metadata-sp.js b/spid-validator/server/api/metadata-sp.js index 352f46b..70c7b48 100644 --- a/spid-validator/server/api/metadata-sp.js +++ b/spid-validator/server/api/metadata-sp.js @@ -1,4 +1,7 @@ const fs = require('fs-extra'); +const multer = require('multer'); +const upload = multer({dest: 'temp/'}); +const unzip = require('unzip'); const Utility = require('../lib/utils'); const MetadataParser = require('../lib/saml-utils').MetadataParser; const config_dir = require('../../config/dir.json'); @@ -76,6 +79,40 @@ module.exports = function(app, checkAuthorisation, getEntityDir, database) { res.status(200).send(result); }) + function setMetadataFromFile(name, tempfile, url, organization) { + let xml = fs.readFileSync(getEntityDir(config_dir.TEMP) + "/" + tempfile, "utf8"); + let metadataParser = new MetadataParser(xml); + + let entityID = metadataParser.getServiceProviderEntityId(); + if(entityID==null || entityID=='') throw new Error("EntityID non specificato"); + + let organization_description = metadataParser.getOrganization().displayName; + if(organization_description==null || organization_description=='') throw new Error("Organization non definito"); + + let metadata_type = metadataParser.isMetadataForAggregated()? 'AG':'SP'; + + let organization_aggregated = undefined; + if(metadataParser.isMetadataForAggregated()) { + organization_aggregated = metadataParser.getSPIDAggregatedContactPerson(); + } + + metadata = { + name: name, + type: metadata_type, + entity_id: entityID, + organization_code: organization, + organization_description: organization_description, + organization_aggregated: organization_aggregated, + url: url, + xml: xml + } + + fs.copyFileSync(getEntityDir(config_dir.TEMP) + "/" + tempfile, getEntityDir(entityID) + "/sp-metadata.xml"); + fs.unlinkSync(getEntityDir(config_dir.TEMP) + "/" + tempfile); + + return metadata; + } + // download metadata app.post("/api/metadata-sp/download", function(req, res) { @@ -104,45 +141,21 @@ module.exports = function(app, checkAuthorisation, getEntityDir, database) { let metadata = { url: req.body.url, xml: null - } + } Utility.metadataDownload(req.body.url, getEntityDir(config_dir.TEMP) + "/" + tempfilename) .then((file_name) => { try { - let xml = fs.readFileSync(getEntityDir(config_dir.TEMP) + "/" + tempfilename, "utf8"); - let metadataParser = new MetadataParser(xml); - - let entityID = metadataParser.getServiceProviderEntityId(); - if(entityID==null || entityID=='') throw new Error("EntityID non specificato"); - - let organization_description = metadataParser.getOrganization().displayName; - if(organization_description==null || organization_description=='') throw new Error("Organization non definito"); - - let metadata_type = metadataParser.isMetadataForAggregated()? 'AG':'SP'; - - let organization_aggregated = undefined; - if(metadataParser.isMetadataForAggregated()) { - organization_aggregated = metadataParser.getSPIDAggregatedContactPerson(); - } - - - metadata = { - type: metadata_type, - entity_id: entityID, - organization_code: organization, - organization_description: organization_description, - organization_aggregated: organization_aggregated, - url: req.body.url, - xml: xml - } - + let metadata = setMetadataFromFile( + req.body.url, + tempfilename, + req.body.url, + organization + ); req.session.metadata = metadata; - fs.copyFileSync(getEntityDir(config_dir.TEMP) + "/" + tempfilename, getEntityDir(entityID) + "/sp-metadata.xml"); - database.setMetadata(user, organization, entityID, external_code, store_type, req.body.url, xml); - fs.unlinkSync(getEntityDir(config_dir.TEMP) + "/" + tempfilename); - - let result = (authorisation=='API')? metadata : xml; + database.setMetadata(user, organization, metadata.entity_id, external_code, store_type, metadata.url, metadata.xml); + let result = (authorisation=='API')? metadata : metadata.xml; res.status(200).send(result); } catch(exception) { @@ -162,6 +175,144 @@ module.exports = function(app, checkAuthorisation, getEntityDir, database) { }); }); + + // carica il file zip + app.post('/api/metadata-sp/upload/zip', upload.single('file'), function (req, res, next) { + + // check if apikey is correct + let authorisation = checkAuthorisation(req); + if(!authorisation) { + error = {code: 401, msg: "Unauthorized"}; + res.status(error.code).send(error.msg); + return null; + } + + if(!req.file) { return res.status(500).send("Please upload a valid zip file"); } + if(authorisation=='API' && !req.body.user) { return res.status(400).send("Parameter user is missing"); } + if(authorisation=='API' && !req.body.organization) { return res.status(400).send("Parameter organization is missing"); } + if(authorisation=='API' && !req.query.store_type) { return res.status(400).send("Parameter store_type is missing"); } + //if(authorisation=='API' && !req.body.external_code) { return res.status(400).send("Parameter external_code is missing"); } + + let user = (authorisation=='API')? req.body.user : req.session.user; + let organization = (authorisation=='API')? req.body.organization : (req.session.entity)? req.session.entity.id : null; + let external_code = (authorisation=='API')? req.body.external_code : req.session.external_code; + let store_type = (authorisation=='API')? req.query.store_type : + (req.session.metadata && req.session.metadata.store_type)? req.session.metadata.store_type : 'main'; + + + Utility.log("POST ZIP", req.file); + if (req.file.mimetype !== "application/zip" && req.file.mimetype !== "application/x-zip-compressed") { + res.status(500).send(`Si è verificato un errore durante il caricamento del metadato, il formato del file fornito non è supportato (${req.file.mimetype})`); + fs.rmSync(req.file.path); + return; + } + + fs.createReadStream(req.file.path) + .pipe(unzip.Extract({path: getEntityDir(config_dir.TEMP)})) + .on('error', (err) => Utility.log('ERRORE FILE: ', err)) + .on('close', async () => { + + let metadata_list = []; + const files = Utility.readDir(getEntityDir(config_dir.TEMP)); + const saveFilePromises = files.map(async (file) => { + + Utility.log("CHECK METADATA FILE from ZIP: ", file); + let metadata = setMetadataFromFile( + file, + file, + getEntityDir(config_dir.TEMP) + "/" + file, + organization, + ); + //Utility.log('METADATA from ZIP: ', metadata); + database.setMetadata(user, organization, metadata.entity_id, external_code, store_type, metadata.url, metadata.xml); + + let dir_metadata = getEntityDir(metadata.entity_id.normalize()); + let test = "extra"; + let profile = "spid-sp-ag-public-full"; + let production = true; + let reportfile = null; + + switch(test) { + case "strict": reportfile = dir_metadata + "/sp-metadata-strict.json"; break; + case "certs": reportfile = dir_metadata + "/sp-metadata-certs.json"; break; + case "extra": reportfile = dir_metadata + "/sp-metadata-extra.json"; break; + } + + await Utility.metadataCheck(test, dir_metadata, profile, config_idp, production).then( + (out) => { + try { + let report = fs.readFileSync(reportfile, "utf8"); + report = JSON.parse(report); + + let lastcheck = { + datetime: moment().format('YYYY-MM-DD HH:mm:ss'), + profile: profile, + report: report, + production: production + } + + let validation = null; + + if(user && metadata.entity_id) { + // save result validation on store + let testGroups = []; + + switch(test) { + case "strict": testGroups = report.test.sp.metadata_strict.SpidSpMetadataCheck; break; + case "certs": testGroups = report.test.sp.metadata_certs.SpidSpMetadataCheckCerts; break; + case "extra": testGroups = report.test.sp.metadata_extra.SpidSpMetadataCheckExtra; break; + } + + for(let t in testGroups) { + let testGroup = testGroups[t]; + if(testGroup.result!=null) { + if(validation==null) validation = (testGroup.result=='success'); + else validation = validation && (testGroup.result=='success'); + } + } + + database.setMetadataValidation(user, metadata.entity_id, external_code, store_type, test, validation); + database.setMetadataLastCheck(user, metadata.entity_id, external_code, store_type, test, lastcheck); + } + + metadata.lastcheck = lastcheck; + metadata.validation = validation; + + } catch(err) { + Utility.log("ERR /api/metadata-sp/upload/zip", err.toString()); + res.status(500).send("Error while loading report"); + } + }, + (err) => { + Utility.log("ERR /api/metadata-sp/upload/zip", err); + res.status(500).send(err); + } + ); + + metadata_list.push(metadata); + }); + + if (saveFilePromises.length > 0) { + Promise.all(saveFilePromises) + .then(() => { + let validation = metadata_list.reduce((a, m)=> { + return a && m.validation; + }, true); + + res.status(201).send({ + validation: validation, + metadata: metadata_list + }); + }) + .catch((error) => res.status(500).send('Si è verificato un errore durante il caricamento del metadato')) + .finally(() => fs.rmSync(req.file.path, {recursive: true, force: true})); + } else { + fs.rmSync(req.file.path, {recursive: true, force: true}); + res.status(500).send('Non sono presenti file all\'interno dello zip caricato'); + } + }); + } + ); // return last validation from store app.get("/api/metadata-sp/lastcheck/:test", function(req, res) { diff --git a/spid-validator/server/lib/utils.js b/spid-validator/server/lib/utils.js index c669f14..bffdf45 100644 --- a/spid-validator/server/lib/utils.js +++ b/spid-validator/server/lib/utils.js @@ -200,6 +200,17 @@ class Utils { }); }); } + + static readDir(filePath) { + const files = fs.readdirSync(filePath); + const fileArray = []; + files.forEach((file) => { + if (file.split('.')[1] === 'xml') { + fileArray.push(file); + } + }); + return fileArray; + } } module.exports = Utils; diff --git a/spid-validator/server/npm-shrinkwrap.json-original b/spid-validator/server/npm-shrinkwrap.json-original new file mode 100644 index 0000000..8ffd64e --- /dev/null +++ b/spid-validator/server/npm-shrinkwrap.json-original @@ -0,0 +1,7 @@ +{ + "dependencies": { + "graceful-fs": { + "version": "4.2.2" + } + } +} \ No newline at end of file diff --git a/spid-validator/server/package.json b/spid-validator/server/package.json index 3a8df0e..d1307e7 100644 --- a/spid-validator/server/package.json +++ b/spid-validator/server/package.json @@ -1,6 +1,6 @@ { "name": "spid-validator", - "version": "1.9.6", + "version": "1.10.0", "description": "Tool for validating Service Provider compliance to SPID response from Identity Provider", "main": "spid-validator", "author": "Michele D'Amico (damikael) - AgID", @@ -18,10 +18,13 @@ "html-entities": "^1.2.1", "moment": "^2.21.0", "moments": "0.0.2", + "multer": "^1.4.5-lts.1", "node-forge": "^0.10.0", "openid-client": "^2.4.5", "request": "^2.87.0", "sha256": "^0.2.0", + "unzip": "^0.1.11", + "upload": "^1.3.2", "uuidjs": "^4.0.3", "xml-encryption": "^0.11.1", "xmlbuilder": "^9.0.7", From 5e6ae8ad4e0b647afd961bafbe4f74c8cadf5a11 Mon Sep 17 00:00:00 2001 From: damikael Date: Fri, 22 Sep 2023 11:18:34 +0200 Subject: [PATCH 3/5] feat: upload-zip, add check, profile, production selector --- .../client/src/components/Sidebar/_nav.js | 18 +- spid-validator/client/src/services.js | 18 +- .../MetadataSpUploadZip.js | 97 ++++++++-- .../src/views/MetadataSpUploadZip/style.css | 132 +++++++++++++- .../src/views/MetadataSpUploadZip/view.js | 172 ++++++++++++++++-- spid-validator/server/api/metadata-sp.js | 43 ++++- 6 files changed, 437 insertions(+), 43 deletions(-) diff --git a/spid-validator/client/src/components/Sidebar/_nav.js b/spid-validator/client/src/components/Sidebar/_nav.js index d199d16..91965d0 100644 --- a/spid-validator/client/src/components/Sidebar/_nav.js +++ b/spid-validator/client/src/components/Sidebar/_nav.js @@ -11,11 +11,6 @@ export default { url: '/metadata-sp-download', sessionRequired: false, }, - { - name: 'Upload ZIP', - url: '/metadata-sp-upload-zip', - sessionRequired: false, - }, { name: 'Check XSD', url: '/metadata-sp-check-xsd', @@ -40,6 +35,19 @@ export default { } ] }, + { + name: 'Pacchetto ZIP', + icon: 'icon-folder', + open: true, + sessionRequired: false, + children: [ + { + name: 'Upload ZIP', + url: '/metadata-sp-upload-zip', + sessionRequired: false, + } + ] + }, { name: 'Request', icon: 'icon-cursor', diff --git a/spid-validator/client/src/services.js b/spid-validator/client/src/services.js index 0812acd..49f1d16 100644 --- a/spid-validator/client/src/services.js +++ b/spid-validator/client/src/services.js @@ -140,10 +140,13 @@ class MainService { }); } - uploadFile(file, callback_progress, callback_response, callback_error) { + uploadFile(file, check, profile, production, callback_progress, callback_response, callback_error) { Utility.log("POST: /api/metadata-sp/upload/zip"); const formData = new FormData(); formData.append('file', file); + formData.append('check', check); + formData.append('profile', profile); + formData.append('production', production); axios.post(' /api/metadata-sp/upload/zip?apikey=' + Utility.getApikey(), formData, { headers: { 'Content-Type': 'multipart/form-data' }, onUploadProgress: (progressEvent)=>callback_progress(progressEvent) @@ -156,6 +159,19 @@ class MainService { }); } + setSessionMetadata(metadata, callback_response, callback_error) { + Utility.log("PUT /api/metadata-sp"); + axios.put('/api/metadata-sp?apikey=' + Utility.getApikey(), {metadata: metadata}) + .then(function(response) { + Utility.log("setSessionMetadata Success", response.data); + callback_response(response.data); + }) + .catch(function(error) { + Utility.log("setSessionMetadata Error", error.response.data); + callback_error((error.response!=null) ? error.response.data : "Service not available"); + }); + } + getLastCheckMetadataSp(test, callback_response, callback_error) { Utility.log("GET /api/metadata-sp/lastcheck/" + test); axios.get('/api/metadata-sp/lastcheck/' + test + '?apikey=' + Utility.getApikey(), {timeout: 900000}) diff --git a/spid-validator/client/src/views/MetadataSpUploadZip/MetadataSpUploadZip.js b/spid-validator/client/src/views/MetadataSpUploadZip/MetadataSpUploadZip.js index ebc7429..f7d7c3a 100644 --- a/spid-validator/client/src/views/MetadataSpUploadZip/MetadataSpUploadZip.js +++ b/spid-validator/client/src/views/MetadataSpUploadZip/MetadataSpUploadZip.js @@ -12,12 +12,17 @@ class MetadataSpUploadZip extends Component { super(props); this.state = { + detailview: false, + file: null, loading: false, loaded: false, fileName: null, fileSize: null, - fileType: null, - result: "" + fileType: null, + check: "extra", + profile: "spid-sp-ag-public-full", + production: true, + report: null }; } @@ -27,33 +32,68 @@ class MetadataSpUploadZip extends Component { let storeState = store.getState(); } - render() { + render() { return view(this); - } + } + + setDetailView(detailed) { + this.setState({ + detailview: detailed + }); + } + + setCheck(check) { + this.setState({ + check: check + }); + } + + setProfile(profile) { + this.setState({ + profile: profile + }); + } + + setProduction(production) { + this.setState({ + production: production + }); + } uploadMetadataZip(metadata_zip) { let service = Services.getMainService(); this.setState({ + file: metadata_zip, loading: true, loaded: false, fileName: metadata_zip.name, fileSize: metadata_zip.size, - fileType: metadata_zip.type + fileType: metadata_zip.type, + report: null }); Utility.blockUI(true); - service.uploadFile(metadata_zip, + service.uploadFile( + metadata_zip, + this.state.check, + this.state.profile, + this.state.production? 'Y':'N', + (progress)=> { this.setState({progress: (progress.loaded*100)/progress.total}); }, - (uploaded)=> { + (report)=> { this.setState({ + file: metadata_zip, loading: false, - loaded: false, - progress: 0, - result: uploaded + loaded: true, + progress: 0, + check: report.check, + profile: report.profile, + production: report.production, + report: report }); Utility.blockUI(false); @@ -65,8 +105,16 @@ class MetadataSpUploadZip extends Component { isOpen: true }); this.setState({ + file: null, loading: false, - loaded: false + loaded: false, + fileName: null, + fileSize: null, + fileType: null, + check: "extra", + profile: "spid-sp-ag-public-full", + production: true, + report: null }); Utility.blockUI(false); @@ -74,6 +122,33 @@ class MetadataSpUploadZip extends Component { ); } + openMetadata(metadata) { + let service = Services.getMainService(); + let store = ReduxStore.getMain(); + + Utility.blockUI(true); + service.setSessionMetadata(metadata, + (success)=> { + store.dispatch(Actions.setMetadataSpURL(metadata.url)); + store.dispatch(Actions.setMetadataSpXML(metadata.xml)); + this.props.history.push('/metadata-sp-download'); + Utility.blockUI(false); + }, + (error)=> { + Utility.showModal({ + title: "Errore", + body: error, + isOpen: true + }); + Utility.blockUI(false); + } + ); + } + + print() { + Utility.print(this.state.fileName); + } + } export default MetadataSpUploadZip; diff --git a/spid-validator/client/src/views/MetadataSpUploadZip/style.css b/spid-validator/client/src/views/MetadataSpUploadZip/style.css index 669274a..020297c 100644 --- a/spid-validator/client/src/views/MetadataSpUploadZip/style.css +++ b/spid-validator/client/src/views/MetadataSpUploadZip/style.css @@ -1,15 +1,137 @@ -.title { +#MetadataSpUploadZip .title { margin-bottom: 30px!important; } -input.metadata { - +#MetadataSpUploadZip .profile-selector { + width: 100%; } -.code { +#MetadataSpUploadZip .code { margin-top: 20px; } -button { +#MetadataSpUploadZip button { margin: 0 5px!important; } + +#MetadataSpUploadZip .testset { + background: #fff; + padding: 15px; + margin-bottom: 20px; +} + +#MetadataSpUploadZip .test-success { + display: block; + float: left; + width: 30px; + line-height: 23px; + margin: 1px; + border: 1px solid #000; + background-color: green; + color: #fff!important; + font-size: 13px; + text-align: center; + padding: 3px; + cursor: pointer; +} + +#MetadataSpUploadZip .test-warning { + display: block; + float: left; + width: 30px; + line-height: 23px; + margin: 1px; + border: 1px solid #000; + background-color: orange; + color: #000!important; + font-size: 13px; + text-align: center; + padding: 3px; + cursor: pointer; +} + +#MetadataSpUploadZip .test-fail { + display: block; + float: left; + width: 30px; + line-height: 23px; + margin: 1px; + border: 1px solid #000; + background-color: red; + color: #fff!important; + font-size: 13px; + text-align: center; + padding: 3px; + cursor: pointer; +} + +#MetadataSpUploadZip .detail-table { + border:1px solid #999; + width: 100%; +} + +#MetadataSpUploadZip .detail-table th, +#MetadataSpUploadZip .detail-table td { + padding: 5px; +} + +#MetadataSpUploadZip .detail-table td a { + color: #000; +} + +#MetadataSpUploadZip .detail-header { + background: #DDDDDD; +} + +#MetadataSpUploadZip .detail-row { + border:1px solid #000; +} + +#MetadataSpUploadZip .detail-row:hover { + background: #EEEEEE; +} + +#MetadataSpUploadZip .detail-num { + width: 50px; +} + +#MetadataSpUploadZip .detail-result { + width: 100px; +} + +#MetadataSpUploadZip .test-success-dm { + background: green!important; + color: #FFFFFF; + font-weight: bold; +} + +#MetadataSpUploadZip .test-warning-dm { + background: orange!important; + color: #000000; + font-weight: bold; +} + +#MetadataSpUploadZip .test-fail-dm { + background: red!important; + color: #FFFFFF; + font-weight: bold; +} + +#MetadataSpUploadZip .tools { + padding: 30px 5px; + background: #FFFFFF; + margin-left: 15px; +} + +#MetadataSpUploadZip .first-upper { + text-transform:capitalize; +} + +@media print { + #MetadataSpUploadZip .upload-section { + display: none; + } + #MetadataSpUploadZip .tools { + display: none; + } +} \ No newline at end of file diff --git a/spid-validator/client/src/views/MetadataSpUploadZip/view.js b/spid-validator/client/src/views/MetadataSpUploadZip/view.js index 6cef0b3..5c080a6 100644 --- a/spid-validator/client/src/views/MetadataSpUploadZip/view.js +++ b/spid-validator/client/src/views/MetadataSpUploadZip/view.js @@ -6,40 +6,184 @@ import "./style.css"; function view(me) { return ( -
-

Metadata Service Provider

-
+
+

Metadata Service Provider ZIP

+ +
- Metadata ZIP -
-
+ Metadata ZIP File +
+
{!me.state.loading &&
-
} {me.state.loading && -
+
Uploading file... {me.state.progress}%
}
- {me.state.result!="" && + + {me.state.loaded && +
+ {!me.state.detailview && +
+ {me.state.report!=null && +
+
+

File: {me.state.fileName}

+

Num. Metadata: {me.state.report.metadata.length}

+ + {me.state.report.metadata.map((m, i)=> { + return( + {e.preventDefault(); me.openMetadata(m)}} + className={m.validation? "test-success" : "test-fail" } + title={m.name}> {i} + + ); + })} + +
+
+ } +
+ } + + {me.state.detailview && +
+ {me.state.report!=null && +
+
+

File: {me.state.fileName}

+

Num. Metadata: {me.state.report.metadata.length}

+ + + + + + + + {me.state.report.metadata.map((m, i)=> { + return( + + + + + + ); + })} +
#FileResult
{i} + {e.preventDefault(); me.openMetadata(m)}}> + {m.name} + + + {m.validation? "OK" : "KO"} +
+
+
+ } +
+ } + +
+
+
+ + Visualizzazione dettaglio + +
+ +
+ +
+ +
+ +
+ +
+ + Check per Produzione +
+
+ + +
+
+
+
+ } + + + + + {/*me.state.result!="" &&
- +
- } + */}
); } diff --git a/spid-validator/server/api/metadata-sp.js b/spid-validator/server/api/metadata-sp.js index 70c7b48..f6cb234 100644 --- a/spid-validator/server/api/metadata-sp.js +++ b/spid-validator/server/api/metadata-sp.js @@ -207,6 +207,13 @@ module.exports = function(app, checkAuthorisation, getEntityDir, database) { return; } + let check = req.body.check? req.body.check : "extra"; + let profile = req.body.profile? req.body.profile : "spid-sp-ag-public-full"; + let production = (req.body.production && req.body.production=='N')? false : true; + + Utility.log("PROFILE", profile); + Utility.log("PRODUCTION", production); + fs.createReadStream(req.file.path) .pipe(unzip.Extract({path: getEntityDir(config_dir.TEMP)})) .on('error', (err) => Utility.log('ERRORE FILE: ', err)) @@ -223,13 +230,14 @@ module.exports = function(app, checkAuthorisation, getEntityDir, database) { getEntityDir(config_dir.TEMP) + "/" + file, organization, ); - //Utility.log('METADATA from ZIP: ', metadata); + + // reset url to avoid to view path on client + metadata.url = metadata.name; + database.setMetadata(user, organization, metadata.entity_id, external_code, store_type, metadata.url, metadata.xml); let dir_metadata = getEntityDir(metadata.entity_id.normalize()); - let test = "extra"; - let profile = "spid-sp-ag-public-full"; - let production = true; + let test = check; let reportfile = null; switch(test) { @@ -238,7 +246,7 @@ module.exports = function(app, checkAuthorisation, getEntityDir, database) { case "extra": reportfile = dir_metadata + "/sp-metadata-extra.json"; break; } - await Utility.metadataCheck(test, dir_metadata, profile, config_idp, production).then( + await Utility.metadataCheck(test, metadata.entity_id.normalize(), profile, config_idp, production).then( (out) => { try { let report = fs.readFileSync(reportfile, "utf8"); @@ -246,9 +254,10 @@ module.exports = function(app, checkAuthorisation, getEntityDir, database) { let lastcheck = { datetime: moment().format('YYYY-MM-DD HH:mm:ss'), + check: check, profile: profile, - report: report, - production: production + production: production, + report: report } let validation = null; @@ -300,6 +309,9 @@ module.exports = function(app, checkAuthorisation, getEntityDir, database) { }, true); res.status(201).send({ + check: check, + profile: profile, + production: production, validation: validation, metadata: metadata_list }); @@ -313,6 +325,23 @@ module.exports = function(app, checkAuthorisation, getEntityDir, database) { }); } ); + + // imposta il metadata in sessione + app.put('/api/metadata-sp/', function(req, res) { + // check if apikey is correct + let authorisation = checkAuthorisation(req); + if(!authorisation) { + error = {code: 401, msg: "Unauthorized"}; + res.status(error.code).send(error.msg); + return null; + } + + if(!req.body.metadata) { return res.status(400).send("Please give me a valid metadata object"); } + + req.session.metadata = req.body.metadata; + + res.status(200).send(); + }); // return last validation from store app.get("/api/metadata-sp/lastcheck/:test", function(req, res) { From be7cba7e85c49c86c593c95ddac76277084732a3 Mon Sep 17 00:00:00 2001 From: damikael Date: Tue, 10 Oct 2023 19:08:42 +0200 Subject: [PATCH 4/5] feat: better zip upload --- spid-validator/client/src/services.js | 5 +- .../MetadataSpUploadZip.js | 46 ++++++++++++++----- .../src/views/MetadataSpUploadZip/view.js | 2 +- spid-validator/server/package.json | 2 +- 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/spid-validator/client/src/services.js b/spid-validator/client/src/services.js index 49f1d16..ecdce1a 100644 --- a/spid-validator/client/src/services.js +++ b/spid-validator/client/src/services.js @@ -140,7 +140,7 @@ class MainService { }); } - uploadFile(file, check, profile, production, callback_progress, callback_response, callback_error) { + uploadFile(file, check, profile, production, callback_progress_upload, callback_progress_download, callback_response, callback_error) { Utility.log("POST: /api/metadata-sp/upload/zip"); const formData = new FormData(); formData.append('file', file); @@ -149,7 +149,8 @@ class MainService { formData.append('production', production); axios.post(' /api/metadata-sp/upload/zip?apikey=' + Utility.getApikey(), formData, { headers: { 'Content-Type': 'multipart/form-data' }, - onUploadProgress: (progressEvent)=>callback_progress(progressEvent) + onUploadProgress: (progressEvent)=>callback_progress_upload(progressEvent), + onDownloadProgress: (progressEvent)=>callback_progress_download(progressEvent) }) .then(function(response) { callback_response(response.data); diff --git a/spid-validator/client/src/views/MetadataSpUploadZip/MetadataSpUploadZip.js b/spid-validator/client/src/views/MetadataSpUploadZip/MetadataSpUploadZip.js index f7d7c3a..6e793bf 100644 --- a/spid-validator/client/src/views/MetadataSpUploadZip/MetadataSpUploadZip.js +++ b/spid-validator/client/src/views/MetadataSpUploadZip/MetadataSpUploadZip.js @@ -22,7 +22,7 @@ class MetadataSpUploadZip extends Component { check: "extra", profile: "spid-sp-ag-public-full", production: true, - report: null + report: null }; } @@ -58,7 +58,13 @@ class MetadataSpUploadZip extends Component { this.setState({ production: production }); + this.setState({ + production: production + }, ()=> { + this.uploadMetadataZip(this.state.file); + }); } + uploadMetadataZip(metadata_zip) { @@ -74,15 +80,33 @@ class MetadataSpUploadZip extends Component { report: null }); - Utility.blockUI(true); + Utility.blockUI(true); service.uploadFile( - metadata_zip, - this.state.check, - this.state.profile, - this.state.production? 'Y':'N', + metadata_zip, + this.state.check, + this.state.profile, + this.state.production? 'Y':'N', (progress)=> { - this.setState({progress: (progress.loaded*100)/progress.total}); + let progress_percent = Math.round((parseInt(progress.loaded)*100)/parseInt(progress.total)); + if(progress_percent<100) { + this.setState({ + progress_message: "Uploading ZIP file...", + progress: progress_percent + "%" + }); + } else { + this.setState({ + progress_message: "Validating all metadata. Please wait...", + progress: "" + }); + } + }, + (progress)=> { + let progress_percent = Math.round((parseInt(progress.loaded)*100)/parseInt(progress.total)); + this.setState({ + progress_message: "Downloading report...", + progress: progress_percent + '%' + }); }, (report)=> { this.setState({ @@ -93,10 +117,10 @@ class MetadataSpUploadZip extends Component { check: report.check, profile: report.profile, production: report.production, - report: report + report: report }); - Utility.blockUI(false); + Utility.blockUI(false); }, (error)=> { Utility.showModal({ @@ -115,9 +139,9 @@ class MetadataSpUploadZip extends Component { profile: "spid-sp-ag-public-full", production: true, report: null - }); + }); - Utility.blockUI(false); + Utility.blockUI(false); } ); } diff --git a/spid-validator/client/src/views/MetadataSpUploadZip/view.js b/spid-validator/client/src/views/MetadataSpUploadZip/view.js index 5c080a6..6482927 100644 --- a/spid-validator/client/src/views/MetadataSpUploadZip/view.js +++ b/spid-validator/client/src/views/MetadataSpUploadZip/view.js @@ -27,7 +27,7 @@ function view(me) { } {me.state.loading &&
- Uploading file... {me.state.progress}% + {me.state.progress_message} {me.state.progress}
}
diff --git a/spid-validator/server/package.json b/spid-validator/server/package.json index d1307e7..bfc8901 100644 --- a/spid-validator/server/package.json +++ b/spid-validator/server/package.json @@ -1,6 +1,6 @@ { "name": "spid-validator", - "version": "1.10.0", + "version": "1.10.1", "description": "Tool for validating Service Provider compliance to SPID response from Identity Provider", "main": "spid-validator", "author": "Michele D'Amico (damikael) - AgID", From 30b79b00158512eb036b6498ec5ac49514bde59b Mon Sep 17 00:00:00 2001 From: damikael Date: Wed, 25 Oct 2023 15:24:15 +0200 Subject: [PATCH 5/5] feat: limit max num of files in zip --- spid-validator/server/api/metadata-sp.js | 8 ++++++++ spid-validator/server/package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/spid-validator/server/api/metadata-sp.js b/spid-validator/server/api/metadata-sp.js index f6cb234..7dea3a0 100644 --- a/spid-validator/server/api/metadata-sp.js +++ b/spid-validator/server/api/metadata-sp.js @@ -8,6 +8,8 @@ const config_dir = require('../../config/dir.json'); const config_idp = require("../../config/idp.json"); const config_test = require("../../config/test.json"); const moment = require('moment'); + +const ZIP_MAX_NUM_FILES = 100; module.exports = function(app, checkAuthorisation, getEntityDir, database) { @@ -221,6 +223,12 @@ module.exports = function(app, checkAuthorisation, getEntityDir, database) { let metadata_list = []; const files = Utility.readDir(getEntityDir(config_dir.TEMP)); + + if(files.length>ZIP_MAX_NUM_FILES) { + res.status(400).send(`Il pacchetto zip può contenere massimo ${ZIP_MAX_NUM_FILES} file`); + return; + } + const saveFilePromises = files.map(async (file) => { Utility.log("CHECK METADATA FILE from ZIP: ", file); diff --git a/spid-validator/server/package.json b/spid-validator/server/package.json index bfc8901..edf2bfe 100644 --- a/spid-validator/server/package.json +++ b/spid-validator/server/package.json @@ -1,6 +1,6 @@ { "name": "spid-validator", - "version": "1.10.1", + "version": "1.10.2", "description": "Tool for validating Service Provider compliance to SPID response from Identity Provider", "main": "spid-validator", "author": "Michele D'Amico (damikael) - AgID",