diff --git a/CHANGELOG.md b/CHANGELOG.md index d4065b1..e802ba0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Forced CSS's to not return content type application/ld+json, which induced a CORS error on some CSS server versions (#131). - Queries based on index file now work for any variable, not just ?object (#136). +- Queries based on index file now work for index files requiring authentication (#139). ## [1.2.1] - 2024-06-17 diff --git a/cypress/e2e/sources-from-indexfile.cy.js b/cypress/e2e/sources-from-indexfile.cy.js index 548d8d0..962ed0e 100644 --- a/cypress/e2e/sources-from-indexfile.cy.js +++ b/cypress/e2e/sources-from-indexfile.cy.js @@ -12,7 +12,7 @@ describe("Sources from index file", () => { // Check that it indeed had 3 sources cy.get('.information-box').contains('Sources: 3'); - + // Check if correct data is displayed cy.contains("http://www/example.com/data/component-c01"); cy.contains("Component 1"); @@ -31,7 +31,7 @@ describe("Sources from index file", () => { // Check that all 4 sources were used cy.get('.information-box').contains('Sources: 4'); - + // Check if correct data is still displayed even if one source was unauthorized cy.contains("http://www/example.com/data/component-c01"); cy.contains("Component 1"); @@ -50,11 +50,58 @@ describe("Sources from index file", () => { // Check that the 4 sources were successfully merged cy.get('.information-box').contains('Sources: 4'); - + // Check if correct data is still displayed even if one source was unauthorized and different sources were merged cy.contains("http://www/example.com/data/component-c01"); cy.contains("Component 1"); cy.contains("Material 1"); }); + it("Sources from an unauthorized source. Before and after log in.", () => { + cy.visit("/"); + + cy.intercept('GET', 'http://localhost:8080/example/index-example-texon-only-AUTH').as('getRequest'); + + // Navigate to correct query + cy.contains("For testing only").click(); + cy.contains("Sources from an index file (requiring authentication)").click(); + + // Wait for the request and assert the response + cy.wait('@getRequest').then((interception) => { + expect(interception.response.statusCode).to.equal(401); + }); + + + cy.contains("http://www/example.com/data/component-c01").should("not.exist"); + cy.contains("Component 1").should("not.exist"); + cy.contains("Material 1").should("not.exist"); + + //log in + cy.get('[aria-label="Profile"]').click(); + cy.contains('[role="menuitem"]', "Login").click(); + + cy.get('input[name="idp"]') + .clear(); + cy.get('input[name="idp"]') + .type("http://localhost:8080/example/profile/card#me"); + cy.contains("Login").click(); + + cy.get("input#email").type("hello@example.com"); + cy.get("input#password").type("abc123"); + cy.contains("button", "Log in").click(); + cy.contains("button", "Authorize").click(); + + cy.url().should("eq", "http://localhost:5173/"); + + //now try again + cy.contains("For testing only").click(); + cy.contains("Sources from an index file (requiring authentication)").click(); + + cy.contains("http://www/example.com/data/component-c01").should("not.exist"); + cy.contains("Component 1").should("exist"); + cy.contains("Material 1").should("exist"); + }) + + + }); diff --git a/initial-pod-data/index-example-texon-only-AUTH$.ttl b/initial-pod-data/index-example-texon-only-AUTH$.ttl new file mode 100644 index 0000000..861e946 --- /dev/null +++ b/initial-pod-data/index-example-texon-only-AUTH$.ttl @@ -0,0 +1,7 @@ +@prefix rdfs: . + +<#index-example> rdfs:seeAlso + , + , + +. diff --git a/initial-pod-data/index-example-texon-only-AUTH.acl b/initial-pod-data/index-example-texon-only-AUTH.acl new file mode 100644 index 0000000..5f4d464 --- /dev/null +++ b/initial-pod-data/index-example-texon-only-AUTH.acl @@ -0,0 +1,9 @@ +@prefix acl: . +@prefix foaf: . + +<#owner> + a acl:Authorization; + acl:accessTo <./index-example-texon-only-AUTH>; + acl:agent ; + acl:mode acl:Read, acl:Write, acl:Control. + diff --git a/public/queries/sourceQueries/index_example_texon_only_source_AUTH.rq b/public/queries/sourceQueries/index_example_texon_only_source_AUTH.rq new file mode 100644 index 0000000..927c065 --- /dev/null +++ b/public/queries/sourceQueries/index_example_texon_only_source_AUTH.rq @@ -0,0 +1,8 @@ +PREFIX rdf: +PREFIX rdfs: +PREFIX example: + +SELECT ?object +WHERE { + example:index-example rdfs:seeAlso ?object . +} diff --git a/src/components/InteractionLayout/AuthenticationMenu/LogoutButton.jsx b/src/components/InteractionLayout/AuthenticationMenu/LogoutButton.jsx index bb4207f..72d3e38 100644 --- a/src/components/InteractionLayout/AuthenticationMenu/LogoutButton.jsx +++ b/src/components/InteractionLayout/AuthenticationMenu/LogoutButton.jsx @@ -23,6 +23,7 @@ const LogoutButton = forwardRef((props, ref) => { function handleLogout(event) { event.preventDefault(); if (isLoggedIn) { + redirect("/"); logout(); } else { redirect("/login"); diff --git a/src/config.json b/src/config.json index 6aaece5..746e247 100644 --- a/src/config.json +++ b/src/config.json @@ -170,6 +170,18 @@ "queryLocation": "/sourceQueries/index_example_texon_only_source.rq" } }, + { + "id": "1500", + "queryGroupId": "c-tst", + "queryLocation": "components_materials.rq", + "name": "Sources from an index file (requiring authentication)", + "description": "Query components (including details about materials) with the sources obtained from index files that require authentication to retrieve said sources.", + "sourcesIndex": { + "url": "http://localhost:8080/example/index-example-texon-only-AUTH", + "queryLocation": "/sourceQueries/index_example_texon_only_source_AUTH.rq" + } + }, + { "id": "2000", "queryGroupId": "b-prj", diff --git a/src/dataProvider/SparqlDataProvider.js b/src/dataProvider/SparqlDataProvider.js index 99b8060..7f7aab0 100644 --- a/src/dataProvider/SparqlDataProvider.js +++ b/src/dataProvider/SparqlDataProvider.js @@ -350,11 +350,10 @@ const addComunicaContextSourcesFromSourcesIndex = async (sourcesIndex) => { }else{ queryStringIndexSource = sourcesIndex.queryString; } - + const bindingsStream = await myEngine.queryBindings(queryStringIndexSource, { - sources: [sourcesIndex.url], + ...generateContext({sources: [sourcesIndex.url]}), }); - await new Promise((resolve, reject) => { bindingsStream.on('data', (bindings) => { // the bindings should have exactly one key (any name is allowed) and we accept the value as a source @@ -381,7 +380,6 @@ const addComunicaContextSourcesFromSourcesIndex = async (sourcesIndex) => { return sourcesList; }; - /** * Creates/extends a comunicaContext property in a query * @param {object} query - the query element from the configuration