diff --git a/access-grant/pom.xml b/access-grant/pom.xml index 757e392b555..5499c07212f 100644 --- a/access-grant/pom.xml +++ b/access-grant/pom.xml @@ -24,6 +24,11 @@ inrupt-client-api ${project.version} + + com.inrupt.client + inrupt-client-vocabulary + ${project.version} + commons-io commons-io diff --git a/access-grant/src/main/java/com/inrupt/client/accessgrant/AccessGrantClient.java b/access-grant/src/main/java/com/inrupt/client/accessgrant/AccessGrantClient.java index e5a86203e42..0fb7342c49f 100644 --- a/access-grant/src/main/java/com/inrupt/client/accessgrant/AccessGrantClient.java +++ b/access-grant/src/main/java/com/inrupt/client/accessgrant/AccessGrantClient.java @@ -391,13 +391,13 @@ public CompletionStage> query(final URI age final URI type; final Set supportedTypes; if (AccessGrant.class.isAssignableFrom(clazz)) { - type = ACCESS_GRANT; + type = URI.create("SolidAccessGrant"); supportedTypes = ACCESS_GRANT_TYPES; } else if (AccessRequest.class.isAssignableFrom(clazz)) { - type = ACCESS_REQUEST; + type = URI.create("SolidAccessRequest"); supportedTypes = ACCESS_REQUEST_TYPES; } else if (AccessDenial.class.isAssignableFrom(clazz)) { - type = ACCESS_DENIAL; + type = URI.create("SolidAccessDenial"); supportedTypes = ACCESS_DENIAL_TYPES; } else { throw new AccessGrantException("Unsupported type " + clazz + " in query request"); diff --git a/access-grant/src/main/java/com/inrupt/client/accessgrant/AccessGrantUtils.java b/access-grant/src/main/java/com/inrupt/client/accessgrant/AccessGrantUtils.java new file mode 100644 index 00000000000..ff3bb2e87bc --- /dev/null +++ b/access-grant/src/main/java/com/inrupt/client/accessgrant/AccessGrantUtils.java @@ -0,0 +1,83 @@ +/* + * Copyright 2023 Inrupt Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.inrupt.client.accessgrant; + +import static com.inrupt.client.vocabulary.RDF.type; + +import com.inrupt.client.spi.RDFFactory; +import com.inrupt.client.util.URIBuilder; +import com.inrupt.client.vocabulary.ACP; + +import java.net.URI; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +import org.apache.commons.rdf.api.IRI; +import org.apache.commons.rdf.api.RDF; +import org.apache.commons.rdf.api.Triple; + +/** + * Utility methods for use with the Access Grant module. + **/ +public final class AccessGrantUtils { + + private static final RDF rdf = RDFFactory.getInstance(); + private static final IRI SOLID_ACCESS_GRANT = rdf.createIRI("http://www.w3.org/ns/solid/vc#SolidAccessGrant"); + + private static IRI asIRI(final URI uri) { + return rdf.createIRI(uri.toString()); + } + + public static Set accessControlPolicyTriples(final URI acl, final URI... modes) { + final Set triples = new HashSet<>(); + final IRI a = asIRI(type); + + // Matcher + final IRI matcher = asIRI(URIBuilder.newBuilder(acl).fragment(UUID.randomUUID().toString()).build()); + triples.add(rdf.createTriple(matcher, a, asIRI(ACP.Matcher))); + triples.add(rdf.createTriple(matcher, asIRI(ACP.vc), SOLID_ACCESS_GRANT)); + + // Policy + final IRI policy = asIRI(URIBuilder.newBuilder(acl).fragment(UUID.randomUUID().toString()).build()); + triples.add(rdf.createTriple(policy, a, asIRI(ACP.Policy))); + triples.add(rdf.createTriple(policy, asIRI(ACP.allOf), matcher)); + for (final URI mode : modes ) { + triples.add(rdf.createTriple(policy, asIRI(ACP.allow), asIRI(mode))); + } + + // Access Control + final IRI accessControl = asIRI(URIBuilder.newBuilder(acl).fragment(UUID.randomUUID().toString()).build()); + triples.add(rdf.createTriple(accessControl, a, asIRI(ACP.AccessControl))); + triples.add(rdf.createTriple(accessControl, asIRI(ACP.apply), policy)); + + // Access Control Resource + final IRI subject = asIRI(acl); + triples.add(rdf.createTriple(subject, a, asIRI(ACP.AccessControlResource))); + triples.add(rdf.createTriple(subject, asIRI(ACP.accessControl), accessControl)); + triples.add(rdf.createTriple(subject, asIRI(ACP.memberAccessControl), accessControl)); + return triples; + } + + private AccessGrantUtils() { + // Prevent instantiation + } +} diff --git a/integration/base/src/main/java/com/inrupt/client/integration/base/AccessGrantScenarios.java b/integration/base/src/main/java/com/inrupt/client/integration/base/AccessGrantScenarios.java index 91677610d1c..1456c505dd6 100644 --- a/integration/base/src/main/java/com/inrupt/client/integration/base/AccessGrantScenarios.java +++ b/integration/base/src/main/java/com/inrupt/client/integration/base/AccessGrantScenarios.java @@ -25,12 +25,12 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import com.inrupt.client.Headers; import com.inrupt.client.Request; import com.inrupt.client.Response; import com.inrupt.client.accessgrant.AccessGrant; import com.inrupt.client.accessgrant.AccessGrantClient; import com.inrupt.client.accessgrant.AccessGrantSession; +import com.inrupt.client.accessgrant.AccessGrantUtils; import com.inrupt.client.accessgrant.AccessRequest; import com.inrupt.client.auth.Credential; import com.inrupt.client.auth.Session; @@ -40,7 +40,6 @@ import com.inrupt.client.spi.RDFFactory; import com.inrupt.client.util.URIBuilder; import com.inrupt.client.vocabulary.ACL; -import com.inrupt.client.vocabulary.ACP; import com.inrupt.client.webid.WebIdProfile; import java.io.ByteArrayInputStream; @@ -113,7 +112,9 @@ public class AccessGrantScenarios { private static String testRDFresourceName = "resource.ttl"; private static URI testRDFresourceURI; private static String sharedTextFileName = "sharedFile.txt"; + private static String sharedResourceName = "sharedResource"; private static URI sharedTextFileURI; + private static URI sharedResource; private static Session session; @BeforeAll @@ -159,32 +160,45 @@ static void setup() throws IOException { sharedTextFileURI = URIBuilder.newBuilder(URI.create(testContainerURI.toString())) .path(sharedTextFileName) .build(); + sharedResource = URIBuilder.newBuilder(URI.create(testContainerURI.toString())) + .path(sharedResourceName).build(); testRDFresourceURI = URIBuilder.newBuilder(testContainerURI) .path(testRDFresourceName) .build(); + session = OpenIdSession.ofClientCredentials( + URI.create(issuer), //Client credentials + CLIENT_ID, + CLIENT_SECRET, + AUTH_METHOD); + //create test file in test container try (final InputStream is = new ByteArrayInputStream(StandardCharsets.UTF_8.encode("Test text").array())) { final SolidNonRDFSource testResource = new SolidNonRDFSource(sharedTextFileURI, Utils.PLAIN_TEXT, is, null); - session = OpenIdSession.ofClientCredentials( - URI.create(issuer), //Client credentials - CLIENT_ID, - CLIENT_SECRET, - AUTH_METHOD); final SolidSyncClient authClient = client.session(session); assertDoesNotThrow(() -> authClient.create(testResource)); prepareACPofResource(authClient, sharedTextFileURI); } - accessGrantServer = new MockAccessGrantServer(State.WEBID.toString(), sharedTextFileURI.toString()); + accessGrantServer = new MockAccessGrantServer(State.WEBID, sharedTextFileURI, sharedResource); accessGrantServer.start(); VC_PROVIDER = config .getOptionalValue("inrupt.test.vc.provider", String.class) .orElse(accessGrantServer.getMockServerUrl()); + final AccessGrantClient accessGrantClient = new AccessGrantClient(URI.create(VC_PROVIDER)).session(session); + final Set modes = new HashSet<>(Arrays.asList(GRANT_MODE_READ, GRANT_MODE_APPEND)); + final Instant expiration = Instant.parse(GRANT_EXPIRATION); + + final AccessRequest request = accessGrantClient.requestAccess(URI.create(webidUrl), + new HashSet<>(Arrays.asList(sharedResource)), modes, PURPOSES, expiration) + .toCompletableFuture().join(); + final AccessGrant grant = accessGrantClient.grantAccess(request) + .toCompletableFuture().join(); + LOGGER.info("Integration Test Issuer: [{}]", issuer); LOGGER.info("Integration Test Pod Host: [{}]", podUrl); LOGGER.info("Integration Test Access Grant server: [{}]", VC_PROVIDER); @@ -230,8 +244,7 @@ void accessGrantIssuanceLifecycleTest(final Session session) { //2. call verify endpoint to verify grant - final URI uri = URIBuilder.newBuilder(URI.create(VC_PROVIDER)).path(grant.getIdentifier().toString()).build(); - final AccessGrant grantFromVcProvider = accessGrantClient.fetch(uri, AccessGrant.class) + final AccessGrant grantFromVcProvider = accessGrantClient.fetch(grant.getIdentifier(), AccessGrant.class) .toCompletableFuture().join(); assertEquals(grant.getPurposes(), grantFromVcProvider.getPurposes()); @@ -260,7 +273,6 @@ void accessGrantIssuanceLifecycleTest(final Session session) { assertDoesNotThrow(accessGrantClient.revoke(grant).toCompletableFuture()::join); //6. call verify endpoint to check the grant is not valid - } @ParameterizedTest @@ -280,8 +292,6 @@ void accessGrantWithRequestOverridesTest(final Session session) { .toCompletableFuture().join(); //2. call verify endpoint to verify grant - - assertDoesNotThrow(accessGrantClient.delete(grant).toCompletableFuture()::join); } @ParameterizedTest @@ -301,8 +311,6 @@ void accessGrantNonRecursiveTest(final Session session) { .toCompletableFuture().join(); //Steps //1. call verify endpoint to verify grant - - assertDoesNotThrow(accessGrantClient.delete(grant).toCompletableFuture()::join); } // Query access grant related tests @@ -316,7 +324,7 @@ void accessGrantQueryByRequestorTest(final Session session) { //query for all grants issued by the user final List grants = accessGrantClient.query(URI.create(webidUrl), - sharedTextFileURI, PURPOSE1, GRANT_MODE_READ, AccessRequest.class) + sharedResource, PURPOSE1, GRANT_MODE_READ, AccessRequest.class) .toCompletableFuture().join(); // result is 4 because we retrieve the grants for each path // sharedTextFileURI = @@ -325,7 +333,7 @@ void accessGrantQueryByRequestorTest(final Session session) { //query for all grants issued by a random user final List randomGrants = accessGrantClient.query(URI.create("https://someuser.test"), - sharedTextFileURI, PURPOSE1, GRANT_MODE_READ, AccessRequest.class) + sharedResource, PURPOSE1, GRANT_MODE_READ, AccessRequest.class) .toCompletableFuture().join(); assertEquals(0, randomGrants.size()); } @@ -340,7 +348,7 @@ void accessGrantQueryByResourceTest(final Session session) { //query for all grants of a dedicated resource final List requests = accessGrantClient.query(URI.create(webidUrl), - sharedTextFileURI, PURPOSE1, GRANT_MODE_READ, AccessRequest.class) + sharedResource, PURPOSE1, GRANT_MODE_READ, AccessRequest.class) .toCompletableFuture().join(); assertEquals(1, requests.size()); @@ -361,14 +369,13 @@ void accessGrantQueryByPurposeTest(final Session session) { //query for all grants with a dedicated purpose final List requests = accessGrantClient.query(URI.create(webidUrl), - sharedTextFileURI, PURPOSE1, GRANT_MODE_READ, AccessRequest.class) + sharedResource, PURPOSE1, GRANT_MODE_READ, AccessRequest.class) .toCompletableFuture().join(); assertEquals(1, requests.size()); - //query for all grants of an unsupported purpose - final URI purpose = URI.create("https://example.com/12"); - final List randomGrants = accessGrantClient.query(URI.create(webidUrl), - sharedTextFileURI, purpose, GRANT_MODE_READ, AccessRequest.class) + //query for all grants of dedicated purpose combinations + final List randomGrants = accessGrantClient.query(URI.create(webidUrl), + sharedResource, PURPOSE1, GRANT_MODE_WRITE, AccessGrant.class) .toCompletableFuture().join(); assertEquals(0, randomGrants.size()); //our grant is actually a Read } @@ -449,7 +456,6 @@ void accessGrantSetRdfTest(final Session session) { authClient.delete(testRDFresourceURI); assertDoesNotThrow(accessGrantClient.revoke(grant).toCompletableFuture()::join); - assertDoesNotThrow(accessGrantClient.delete(grant).toCompletableFuture()::join); } } @@ -528,7 +534,6 @@ void accessGrantGetNonRdfTest(final Session session) throws IOException { authClient.delete(newTestFileURI); assertDoesNotThrow(accessGrantClient.revoke(grant).toCompletableFuture()::join); - assertDoesNotThrow(accessGrantClient.delete(grant).toCompletableFuture()::join); } @ParameterizedTest @@ -582,7 +587,6 @@ void accessGrantSetNonRdfTest(final Session session) throws IOException { authClient.delete(newTestFileURI); assertDoesNotThrow(accessGrantClient.revoke(grant).toCompletableFuture()::join); - assertDoesNotThrow(accessGrantClient.delete(grant).toCompletableFuture()::join); } @ParameterizedTest @@ -620,60 +624,15 @@ void accessGrantCreateNonRdfTest(final Session session) throws IOException { private static void prepareACPofResource(final SolidSyncClient authClient, final URI resourceURI) { - final IRI acpAllOf = rdf.createIRI(ACP.allOf.toString()); - final IRI acpVc = rdf.createIRI(ACP.vc.toString()); - final IRI acpAllow = rdf.createIRI(ACP.allow.toString()); - final IRI acpApply = rdf.createIRI(ACP.apply.toString()); - final IRI acpAccessControl = rdf.createIRI(ACP.accessControl.toString()); - final IRI aclRead = rdf.createIRI(ACL.Read.toString()); - final IRI aclWrite = rdf.createIRI(ACL.Write.toString()); - // find the acl Link in the header of the resource - final Request req = Request.newBuilder(resourceURI) - .HEAD() - .build(); - final Response res = authClient.send(req, Response.BodyHandlers.discarding()); - final Headers.Link acrLink = res.headers().allValues("Link").stream() - .flatMap(l -> Headers.Link.parse(l).stream()) - .filter(link -> link.getParameter("rel").contains("acl")) - .findAny() - .orElse(null); - - // add the triples needed for access grant - if (acrLink != null) { - final URI resourceACRurl = acrLink.getUri(); - final IRI resourceACRiri = rdf.createIRI(resourceACRurl.toString()); - - //read the existing triples and add them to the dataset - try (final SolidRDFSource resource = authClient.read(resourceACRurl, SolidRDFSource.class)) { - - //creating a new matcher - final URI newMatcherURI = URIBuilder.newBuilder(resourceACRurl).fragment("newMatcher").build(); - final IRI newMatcher = rdf.createIRI(newMatcherURI.toString()); - final IRI solidAccessGrant = rdf.createIRI("http://www.w3.org/ns/solid/vc#SolidAccessGrant"); - - resource.add(rdf.createQuad(resourceACRiri, newMatcher, acpVc, solidAccessGrant)); - - //create a new policy - final URI newPolicyURI = URIBuilder.newBuilder(resourceACRurl).fragment("newPolicy").build(); - final IRI newPolicy = rdf.createIRI(newPolicyURI.toString()); - - resource.add(rdf.createQuad(resourceACRiri, newPolicy, acpAllOf, newMatcher)); - resource.add(rdf.createQuad(resourceACRiri, newPolicy, acpAllow, aclRead)); - resource.add(rdf.createQuad(resourceACRiri, newPolicy, acpAllow, aclWrite)); - - //creating a new access control - final URI newAccessControlURI = - URIBuilder.newBuilder(resourceACRurl).fragment("newAccessControl").build(); - final IRI newAccessControl = rdf.createIRI(newAccessControlURI.toString()); - - resource.add(rdf.createQuad(resourceACRiri, newAccessControl, acpApply, newPolicy)); - - //adding the new access control to the ACP - resource.add(rdf.createQuad(resourceACRiri, resourceACRiri, acpAccessControl, newAccessControl)); - - authClient.update(resource); - } + try (final SolidNonRDFSource resource = authClient.read(resourceURI, SolidNonRDFSource.class)) { + resource.getMetadata().getAcl().ifPresent(acl -> { + try (final SolidRDFSource acr = authClient.read(acl, SolidRDFSource.class)) { + AccessGrantUtils.accessControlPolicyTriples(acl, ACL.Read, ACL.Write) + .forEach(acr.getGraph()::add); + authClient.update(acr); + } + }); } } @@ -687,8 +646,8 @@ private static Stream provideSessions() throws SolidClientException { final var token = credential.map(Credential::getToken) .orElseThrow(() -> new OpenIdException("We could not get a token")); return Stream.of( - Arguments.of(OpenIdSession.ofIdToken(token), //OpenId token + Arguments.of(OpenIdSession.ofIdToken(token)), //OpenId token Arguments.of(session) - )); + ); } } diff --git a/integration/base/src/main/java/com/inrupt/client/integration/base/AuthenticationScenarios.java b/integration/base/src/main/java/com/inrupt/client/integration/base/AuthenticationScenarios.java index b6c15fc7a66..cc7ce076d17 100644 --- a/integration/base/src/main/java/com/inrupt/client/integration/base/AuthenticationScenarios.java +++ b/integration/base/src/main/java/com/inrupt/client/integration/base/AuthenticationScenarios.java @@ -203,7 +203,7 @@ void fetchPrivateResourceUnauthenticatedTest(final Session session) { @MethodSource("provideSessions") @DisplayName(":authenticatedPublicNode Authenticated fetch of public resource succeeds") void fetchPublicResourceAuthenticatedTest(final Session session) { - LOGGER.info("Integration Test - AuAuthenticatedth fetch of public resource"); + LOGGER.info("Integration Test - Authenticated fetch of public resource"); //create public resource final SolidSyncClient client = SolidSyncClient.getClient(); try (final SolidRDFSource testResource = new SolidRDFSource(publicResourceURL, null, null)) { diff --git a/integration/base/src/main/java/com/inrupt/client/integration/base/MockAccessGrantServer.java b/integration/base/src/main/java/com/inrupt/client/integration/base/MockAccessGrantServer.java index a4d1ddfbc94..90662f67f5f 100644 --- a/integration/base/src/main/java/com/inrupt/client/integration/base/MockAccessGrantServer.java +++ b/integration/base/src/main/java/com/inrupt/client/integration/base/MockAccessGrantServer.java @@ -29,19 +29,23 @@ import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; +import java.net.URI; import org.apache.commons.io.IOUtils; class MockAccessGrantServer { - private final WireMockServer wireMockServer; - private String webId; - private String sharedFile; - private final String DERIVE = "/derive"; + private static final String DERIVE = "/derive"; - public MockAccessGrantServer(final String webId, final String sharedFile) { - this.webId = webId; - this.sharedFile = sharedFile; + private final WireMockServer wireMockServer; + private final String webId; + private final String sharedFile; + private final String sharedResource; + + public MockAccessGrantServer(final URI webId, final URI sharedFile, final URI sharedResource) { + this.webId = webId.toString(); + this.sharedFile = sharedFile.toString(); + this.sharedResource = sharedResource.toString(); wireMockServer = new WireMockServer(WireMockConfiguration.options().dynamicPort()); } @@ -52,14 +56,14 @@ private void setupMocks() { .withHeader(Utils.CONTENT_TYPE, Utils.APPLICATION_JSON) .withBody(getResource("/vc-configuration.json", wireMockServer.baseUrl())))); - wireMockServer.stubFor(get(urlPathMatching(".+:(\\d*)/vc-grant")) + wireMockServer.stubFor(get(urlPathEqualTo("/vc-grant")) .willReturn(aResponse() .withStatus(Utils.SUCCESS) .withHeader(Utils.CONTENT_TYPE, Utils.APPLICATION_JSON) .withBody(getResource("/vc-grant.json", wireMockServer.baseUrl(), this.webId, this.sharedFile)))); - wireMockServer.stubFor(get(urlPathMatching(".+:(\\d*)/vc-request")) + wireMockServer.stubFor(get(urlPathEqualTo("/vc-request")) .willReturn(aResponse() .withStatus(Utils.SUCCESS) .withHeader(Utils.CONTENT_TYPE, Utils.APPLICATION_JSON) @@ -71,6 +75,27 @@ private void setupMocks() { .withStatus(204))); wireMockServer.stubFor(post(urlEqualTo("/issue")) + .atPriority(2) + .withRequestBody(containing("hasConsent")) + .withRequestBody(containing(this.sharedResource)) + .willReturn(aResponse() + .withStatus(Utils.SUCCESS) + .withHeader(Utils.CONTENT_TYPE, Utils.APPLICATION_JSON) + .withBody(getResource("/vc-request.json", wireMockServer.baseUrl(), + this.webId, this.sharedResource)))); + + wireMockServer.stubFor(post(urlEqualTo("/issue")) + .atPriority(2) + .withRequestBody(containing("providedConsent")) + .withRequestBody(containing(this.sharedResource)) + .willReturn(aResponse() + .withStatus(Utils.SUCCESS) + .withHeader(Utils.CONTENT_TYPE, Utils.APPLICATION_JSON) + .withBody(getResource("/vc-grant.json", wireMockServer.baseUrl(), + this.webId, this.sharedResource)))); + + wireMockServer.stubFor(post(urlEqualTo("/issue")) + .atPriority(1) .withRequestBody(containing("hasConsent")) .willReturn(aResponse() .withStatus(Utils.SUCCESS) @@ -79,6 +104,7 @@ private void setupMocks() { this.webId, this.sharedFile)))); wireMockServer.stubFor(post(urlEqualTo("/issue")) + .atPriority(1) .withRequestBody(containing("providedConsent")) .willReturn(aResponse() .withStatus(Utils.SUCCESS) @@ -96,6 +122,17 @@ private void setupMocks() { .withRequestBody(containing("\"Read\"")) .withRequestBody(containing("\"https://purpose.example/212efdf4-e1a4-4dcd-9d3b-d6eb92e0205f\"")) .withRequestBody(containing("\"" + this.webId + "\"")) + .withRequestBody(containing("\"" + this.sharedResource + "\"")) + .willReturn(aResponse() + .withStatus(Utils.SUCCESS) + .withHeader(Utils.CONTENT_TYPE, Utils.APPLICATION_JSON) + .withBody(getResource("/query_response.json", wireMockServer.baseUrl(), + this.webId, this.sharedResource)))); + + wireMockServer.stubFor(post(urlEqualTo(DERIVE)) + .atPriority(1) + .withRequestBody(containing("\"Read\"")) + .withRequestBody(containing("\"" + this.webId + "\"")) .withRequestBody(containing("\"" + this.sharedFile + "\"")) .willReturn(aResponse() .withStatus(Utils.SUCCESS) @@ -113,7 +150,7 @@ private void setupMocks() { } - private String getResource(final String path) { + private static String getResource(final String path) { try (final InputStream res = MockAccessGrantServer.class.getResourceAsStream(path)) { return new String(IOUtils.toByteArray(res), UTF_8); } catch (final IOException ex) { @@ -121,11 +158,12 @@ private String getResource(final String path) { } } - private String getResource(final String path, final String baseUrl) { + private static String getResource(final String path, final String baseUrl) { return getResource(path).replace("{{baseUrl}}", baseUrl); } - private String getResource(final String path, final String baseUrl, final String webId, final String sharedFile) { + private static String getResource(final String path, final String baseUrl, final String webId, + final String sharedFile) { return getResource(path).replace("{{baseUrl}}", baseUrl) .replace("{{webId}}", webId) .replace("{{sharedFile}}", sharedFile);