diff --git a/src/main/java/org/qortal/api/resource/RenderResource.java b/src/main/java/org/qortal/api/resource/RenderResource.java index ae8bade9..5b2b1333 100644 --- a/src/main/java/org/qortal/api/resource/RenderResource.java +++ b/src/main/java/org/qortal/api/resource/RenderResource.java @@ -25,6 +25,7 @@ import org.qortal.api.Security; import org.qortal.arbitrary.misc.Service; import org.qortal.arbitrary.*; import org.qortal.arbitrary.exception.MissingDataException; +import org.qortal.controller.arbitrary.ArbitraryDataRenderManager; import org.qortal.data.transaction.ArbitraryTransactionData.*; import org.qortal.repository.DataException; import org.qortal.settings.Settings; @@ -94,11 +95,34 @@ public class RenderResource { return "Unable to generate preview URL"; } + @POST + @Path("authorize/{service}/{resourceId}") + @SecurityRequirement(name = "apiKey") + public boolean authorizeResource(@PathParam("service") Service service, + @PathParam("resourceId") String resourceId) { + Security.checkApiCallAllowed(request); + ArbitraryDataResource resource = new ArbitraryDataResource(resourceId, null, service, null); + ArbitraryDataRenderManager.getInstance().addToAuthorizedResources(resource); + return true; + } + + @POST + @Path("authorize/{service}/{resourceId}/{identifier}") + @SecurityRequirement(name = "apiKey") + public boolean authorizeResource(@PathParam("service") Service service, + @PathParam("resourceId") String resourceId, + @PathParam("identifier") String identifier) { + Security.checkApiCallAllowed(request); + ArbitraryDataResource resource = new ArbitraryDataResource(resourceId, null, service, identifier); + ArbitraryDataRenderManager.getInstance().addToAuthorizedResources(resource); + return true; + } + @GET @Path("/signature/{signature}") @SecurityRequirement(name = "apiKey") public HttpServletResponse getIndexBySignature(@PathParam("signature") String signature) { - Security.checkApiCallAllowed(request); + requirePriorAuthorization(signature, Service.WEBSITE, null); return this.get(signature, ResourceIdType.SIGNATURE, null, "/", null, "/render/signature", true, true); } @@ -106,7 +130,7 @@ public class RenderResource { @Path("/signature/{signature}/{path:.*}") @SecurityRequirement(name = "apiKey") public HttpServletResponse getPathBySignature(@PathParam("signature") String signature, @PathParam("path") String inPath) { - Security.checkApiCallAllowed(request); + requirePriorAuthorization(signature, Service.WEBSITE, null); return this.get(signature, ResourceIdType.SIGNATURE, null, inPath,null, "/render/signature", true, true); } @@ -114,7 +138,7 @@ public class RenderResource { @Path("/hash/{hash}") @SecurityRequirement(name = "apiKey") public HttpServletResponse getIndexByHash(@PathParam("hash") String hash58, @QueryParam("secret") String secret58) { - Security.checkApiCallAllowed(request); + requirePriorAuthorization(hash58, Service.WEBSITE, null); return this.get(hash58, ResourceIdType.FILE_HASH, Service.WEBSITE, "/", secret58, "/render/hash", true, false); } @@ -123,7 +147,7 @@ public class RenderResource { @SecurityRequirement(name = "apiKey") public HttpServletResponse getPathByHash(@PathParam("hash") String hash58, @PathParam("path") String inPath, @QueryParam("secret") String secret58) { - Security.checkApiCallAllowed(request); + requirePriorAuthorization(hash58, Service.WEBSITE, null); return this.get(hash58, ResourceIdType.FILE_HASH, Service.WEBSITE, inPath, secret58, "/render/hash", true, false); } @@ -133,7 +157,7 @@ public class RenderResource { public HttpServletResponse getPathByName(@PathParam("service") Service service, @PathParam("name") String name, @PathParam("path") String inPath) { - Security.checkApiCallAllowed(request); + requirePriorAuthorization(name, service, null); String prefix = String.format("/render/%s", service); return this.get(name, ResourceIdType.NAME, service, inPath, null, prefix, true, true); } @@ -143,7 +167,7 @@ public class RenderResource { @SecurityRequirement(name = "apiKey") public HttpServletResponse getIndexByName(@PathParam("service") Service service, @PathParam("name") String name) { - Security.checkApiCallAllowed(request); + requirePriorAuthorization(name, service, null); String prefix = String.format("/render/%s", service); return this.get(name, ResourceIdType.NAME, service, "/", null, prefix, true, true); } @@ -176,4 +200,11 @@ public class RenderResource { return renderer.render(); } + private void requirePriorAuthorization(String resourceId, Service service, String identifier) { + ArbitraryDataResource resource = new ArbitraryDataResource(resourceId, null, service, identifier); + if (!ArbitraryDataRenderManager.getInstance().isAuthorized(resource)) { + throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.UNAUTHORIZED, "Call /render/authorize first"); + } + } + } diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataResource.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataResource.java index 05fc4933..3974939a 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataResource.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataResource.java @@ -113,7 +113,23 @@ public class ArbitraryDataResource { } } + private String resourceIdString() { + return resourceId != null ? resourceId : ""; + } + + private String resourceIdTypeString() { + return resourceIdType != null ? resourceIdType.toString() : ""; + } + + private String serviceString() { + return service != null ? service.toString() : ""; + } + private String identifierString() { return identifier != null ? identifier : ""; } + + public String toString() { + return String.format("%s-%s-%s-%s", resourceIdString(), resourceIdTypeString(), serviceString(), identifierString()); + } } diff --git a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataRenderManager.java b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataRenderManager.java new file mode 100644 index 00000000..693cbf82 --- /dev/null +++ b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataRenderManager.java @@ -0,0 +1,50 @@ +package org.qortal.controller.arbitrary; + +import org.qortal.arbitrary.ArbitraryDataFile; +import org.qortal.arbitrary.ArbitraryDataResource; +import org.qortal.arbitrary.misc.Service; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +public class ArbitraryDataRenderManager { + + private static ArbitraryDataRenderManager instance; + + /** + * List to keep track of authorized resources for rendering. + */ + private List authorizedResources = Collections.synchronizedList(new ArrayList<>()); + + + public ArbitraryDataRenderManager() { + + } + + public static ArbitraryDataRenderManager getInstance() { + if (instance == null) + instance = new ArbitraryDataRenderManager(); + + return instance; + } + + public boolean isAuthorized(ArbitraryDataResource resource) { + for (ArbitraryDataResource authorizedResource : this.authorizedResources) { + if (authorizedResource != null && resource != null) { + if (Objects.equals(authorizedResource.toString(), resource.toString())) { + return true; + } + } + } + return false; + } + + public void addToAuthorizedResources(ArbitraryDataResource resource) { + if (!this.isAuthorized(resource)) { + this.authorizedResources.add(resource); + } + } + +}