Added /render/authorize/{service}/{resourceId}* APIs

These allow the UI to pre-authorize a resource and therefore avoid having to pass a sensitive API key to a website or app.
This commit is contained in:
CalDescent 2021-11-21 14:57:26 +00:00
parent d0000c6131
commit b4f3105035
3 changed files with 103 additions and 6 deletions

View File

@ -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");
}
}
}

View File

@ -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());
}
}

View File

@ -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<ArbitraryDataResource> 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);
}
}
}