Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow delegate system to produce valid HTTP 401 info.json responses #583

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## 5.0.6

* IIIF information endpoints always return JSON in HTTP 4xx responses.
* IIIF information endpoints return JSON in HTTP 401 responses.
* Fixed a bug whereby the values of the `operations` and `page_count` keys
in the delegate context were not set.
* TurboJpegProcessor is able to generate non-JPEG derivative images, which
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,9 @@ private boolean processAuthInfo(AuthInfo info)
if (code == 401) {
getResponse().setHeader("WWW-Authenticate",
info.getChallengeValue());
if (getRequestContext().getLocalURI().getPath().endsWith("info.json")) {
return true;
}
}
throw new ResourceException(new Status(code));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package edu.illinois.library.cantaloupe.resource.iiif.v1;

import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import edu.illinois.library.cantaloupe.http.Method;
Expand Down Expand Up @@ -54,7 +52,16 @@ public void doGET() throws Exception {
class CustomCallback implements InformationRequestHandler.Callback {
@Override
public boolean authorize() throws Exception {
return InformationResource.this.preAuthorize();
try {
// The logic here is somewhat convoluted. See the method
// documentation for more information.
return InformationResource.this.preAuthorize();
} catch (ResourceException e) {
if (e.getStatus().getCode() > 400) {
throw e;
}
}
return false;
}
@Override
public void knowAvailableOutputFormats(Set<Format> formats) {
Expand All @@ -70,25 +77,18 @@ public void knowAvailableOutputFormats(Set<Format> formats) {
.withRequestContext(getRequestContext())
.withCallback(new CustomCallback())
.build()) {
try {
Info info = handler.handle();
ImageInfo iiifInfo = new ImageInfoFactory().newImageInfo(
getImageURI(),
availableOutputFormats,
info,
getPageIndex(),
getMetaIdentifier().getScaleConstraint());
addHeaders(iiifInfo);
new JacksonRepresentation(iiifInfo)
.write(getResponse().getOutputStream());
} catch (ResourceException e) {
if (e.getStatus().getCode() < 500) {
newHTTP4xxRepresentation(e.getStatus(), e.getMessage())
.write(getResponse().getOutputStream());
} else {
throw e;
}
}
Info info = handler.handle();

ImageInfo iiifInfo = new ImageInfoFactory().newImageInfo(
getImageURI(),
availableOutputFormats,
info,
getPageIndex(),
getMetaIdentifier().getScaleConstraint());

addHeaders(iiifInfo);
new JacksonRepresentation(iiifInfo)
.write(getResponse().getOutputStream());
}
}

Expand Down Expand Up @@ -122,14 +122,4 @@ private String getNegotiatedMediaType() {
return mediaType + ";charset=UTF-8";
}

private JacksonRepresentation newHTTP4xxRepresentation(Status status,
String message) {
final Map<String, Object> map = new LinkedHashMap<>(); // preserves key order
map.put("@context", "http://library.stanford.edu/iiif/image-api/1.1/context.json");
map.put("@id", getImageURI());
map.put("status", status.getCode());
map.put("message", message);
return new JacksonRepresentation(map);
}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package edu.illinois.library.cantaloupe.resource.iiif.v2;

import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import edu.illinois.library.cantaloupe.http.Method;
Expand All @@ -18,8 +16,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.script.ScriptException;

/**
* Handles IIIF Image API 2.x information requests.
*
Expand Down Expand Up @@ -61,7 +57,16 @@ public void doGET() throws Exception {
class CustomCallback implements InformationRequestHandler.Callback {
@Override
public boolean authorize() throws Exception {
return InformationResource.this.preAuthorize();
try {
// The logic here is somewhat convoluted. See the method
// documentation for more information.
return InformationResource.this.preAuthorize();
} catch (ResourceException e) {
if (e.getStatus().getCode() > 400) {
throw e;
}
}
return false;
}
@Override
public void knowAvailableOutputFormats(Set<Format> formats) {
Expand All @@ -77,19 +82,10 @@ public void knowAvailableOutputFormats(Set<Format> formats) {
.withRequestContext(getRequestContext())
.withCallback(new CustomCallback())
.build()) {
Info info = handler.handle();
addHeaders();
try {
Info info = handler.handle();
newRepresentation(info, availableOutputFormats)
.write(getResponse().getOutputStream());
} catch (ResourceException e) {
if (e.getStatus().getCode() < 500) {
newHTTP4xxRepresentation(e.getStatus(), e.getMessage())
.write(getResponse().getOutputStream());
} else {
throw e;
}
}
newRepresentation(info, availableOutputFormats)
.write(getResponse().getOutputStream());
}
}

Expand Down Expand Up @@ -135,17 +131,4 @@ private JacksonRepresentation newRepresentation(Info info,
return new JacksonRepresentation(imageInfo);
}

private JacksonRepresentation newHTTP4xxRepresentation(
Status status,
String message) throws ScriptException {
final Map<String,Object> map = new LinkedHashMap<>(); // preserves key order
map.put("@context", "http://iiif.io/api/image/2/context.json");
map.put("@id", getImageURI());
map.put("protocol", "http://iiif.io/api/image");
map.put("status", status.getCode());
map.put("message", message);
map.putAll(getDelegateProxy().getExtraIIIF2InformationResponseKeys());
return new JacksonRepresentation(map);
}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package edu.illinois.library.cantaloupe.resource.iiif.v3;

import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import edu.illinois.library.cantaloupe.http.Method;
Expand All @@ -18,8 +16,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.script.ScriptException;

/**
* Handles IIIF Image API 3.x information requests.
*
Expand Down Expand Up @@ -61,7 +57,16 @@ public void doGET() throws Exception {
class CustomCallback implements InformationRequestHandler.Callback {
@Override
public boolean authorize() throws Exception {
return InformationResource.this.preAuthorize();
try {
// The logic here is somewhat convoluted. See the method
// documentation for more information.
return InformationResource.this.preAuthorize();
} catch (ResourceException e) {
if (e.getStatus().getCode() > 400) {
throw e;
}
}
return false;
}
@Override
public void knowAvailableOutputFormats(Set<Format> formats) {
Expand All @@ -77,19 +82,10 @@ public void knowAvailableOutputFormats(Set<Format> formats) {
.withRequestContext(getRequestContext())
.withCallback(new CustomCallback())
.build()) {
Info info = handler.handle();
addHeaders();
try {
Info info = handler.handle();
newRepresentation(info, availableOutputFormats)
.write(getResponse().getOutputStream());
} catch (ResourceException e) {
if (e.getStatus().getCode() < 500) {
newHTTP4xxRepresentation(e.getStatus(), e.getMessage())
.write(getResponse().getOutputStream());
} else {
throw e;
}
}
newRepresentation(info, availableOutputFormats)
.write(getResponse().getOutputStream());
}
}

Expand Down Expand Up @@ -137,19 +133,4 @@ private JacksonRepresentation newRepresentation(Info info,
return new JacksonRepresentation(imageInfo);
}

private JacksonRepresentation newHTTP4xxRepresentation(
Status status,
String message) throws ScriptException {
final Map<String,Object> map = new LinkedHashMap<>(); // preserves key order
map.put("@context", "http://iiif.io/api/image/3/context.json");
map.put("id", getImageURI());
map.put("type", "ImageService3");
map.put("protocol", "http://iiif.io/api/image");
map.put("profile", "level2");
map.put("status", status.getCode());
map.put("message", message);
map.putAll(getDelegateProxy().getExtraIIIF3InformationResponseKeys());
return new JacksonRepresentation(map);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,20 @@ public void testAuthorizationWhenAuthorized(URI uri) {
assertStatus(200, uri);
}

public void testAuthorizationWhenUnauthorized(URI uri, String endpointPath) {
// This may vary depending on the return value of a delegate method,
// but the way the tests are set up, it's 401.
assertStatus(401, uri);
assertRepresentationContains("401 Unauthorized", uri);
}

public void testAuthorizationWhenForbidden(URI uri) {
// This may vary depending on the return value of a delegate method,
// but the way the tests are set up, it's 403.
assertStatus(403, uri);
assertRepresentationContains("403 Forbidden", uri);
}

public void testAuthorizationWhenNotAuthorizedWhenAccessingCachedResource(URI uri)
throws Exception {
initializeFilesystemCache();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,6 @@
*/
public class ImageResourceTester extends ImageAPIResourceTester {

public void testAuthorizationWhenUnauthorized(URI uri) {
// This may vary depending on the return value of a delegate method,
// but the test delegate script returns 403.
assertStatus(401, uri);
assertRepresentationContains("401 Unauthorized", uri);
}

public void testAuthorizationWhenForbidden(URI uri) {
// This may vary depending on the return value of a delegate method,
// but the test delegate script returns 403.
assertStatus(403, uri);
assertRepresentationContains("403 Forbidden", uri);
}

public void testAuthorizationWhenRedirecting(URI uri)
throws Exception {
Client client = newClient(uri);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import edu.illinois.library.cantaloupe.http.Response;
import edu.illinois.library.cantaloupe.image.Identifier;
import edu.illinois.library.cantaloupe.resource.AbstractResource;
import edu.illinois.library.cantaloupe.resource.Route;
import edu.illinois.library.cantaloupe.test.TestUtil;

import java.io.File;
Expand All @@ -27,11 +28,18 @@
*/
public class InformationResourceTester extends ImageAPIResourceTester {

public void testAuthorizationWhenForbidden(URI uri) {
// This may vary depending on the return value of a delegate method,
// but the test delegate script returns 401.
assertStatus(403, uri);
assertRepresentationContains("\"status\":403", uri);
@Override
public void testAuthorizationWhenUnauthorized(URI uri, String endpointPath) {
final String requiredJsonLdContent;

if (endpointPath.equals(Route.IIIF_1_PATH)) {
requiredJsonLdContent = "\"@context\":\"http://library.stanford.edu/iiif/image-api/1.1/context.json\"";
} else {
requiredJsonLdContent = "\"protocol\":\"http://iiif.io/api/image\"";
}

assertStatus(401, uri);
assertRepresentationContains(requiredJsonLdContent, uri);
}

public void testCacheWithDerivativeCacheEnabledAndInfoCacheEnabledAndResolveFirstEnabled(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ void testGETAuthorizationWhenAuthorized() {
@Test
void testGETAuthorizationWhenUnauthorized() {
URI uri = getHTTPURI("/unauthorized.jpg/full/full/0/color.jpg");
tester.testAuthorizationWhenUnauthorized(uri);
tester.testAuthorizationWhenUnauthorized(uri, getEndpointPath());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,7 @@ void testGETAuthorizationWhenAuthorized() {
@Test
void testGETAuthorizationWhenUnauthorized() {
URI uri = getHTTPURI("/unauthorized.jpg/info.json");
// This may vary depending on the return value of a delegate method,
// but the test delegate script returns 401.
assertStatus(401, uri);
assertRepresentationEquals(
"{\"@context\":\"http://library.stanford.edu/iiif/image-api/1.1/context.json\","+
"\"@id\":\"" + uri.toString().replace("/info.json", "") + "\"," +
"\"status\":401," +
"\"message\":\"Unauthorized\"" +
"}", uri);
tester.testAuthorizationWhenUnauthorized(uri, getEndpointPath());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ void testGETAuthorizationWhenAuthorized() {
@Test
void testGETAuthorizationWhenUnauthorized() {
URI uri = getHTTPURI("/unauthorized.jpg/full/full/0/color.jpg");
tester.testAuthorizationWhenUnauthorized(uri);
tester.testAuthorizationWhenUnauthorized(uri, getEndpointPath());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,22 +51,7 @@ void testGETAuthorizationWhenAuthorized() {
@Test
void testGETAuthorizationWhenUnauthorized() {
URI uri = getHTTPURI("/unauthorized.jpg/info.json");
// This may vary depending on the return value of a delegate method,
// but the test delegate script returns 401.
assertStatus(401, uri);
assertRepresentationEquals("{\"@context\":\"http://iiif.io/api/image/2/context.json\","+
"\"@id\":\"" + uri.toString().replace("/info.json", "") + "\"," +
"\"protocol\":\"http://iiif.io/api/image\"," +
"\"status\":401," +
"\"message\":\"Unauthorized\"," +
"\"attribution\":\"Copyright My Great Organization. All rights reserved.\"," +
"\"license\":\"http://example.org/license.html\"," +
"\"service\":{" +
"\"@context\":\"http://iiif.io/api/annex/services/physdim/1/context.json\"," +
"\"profile\":\"http://iiif.io/api/annex/services/physdim\"," +
"\"physicalScale\":0.0025," +
"\"physicalUnits\":\"in\"}" +
"}", uri);
tester.testAuthorizationWhenUnauthorized(uri, getEndpointPath());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ void testGETAuthorizationWhenAuthorized() {
@Test
void testGETAuthorizationWhenUnauthorized() {
URI uri = getHTTPURI("/unauthorized.jpg/full/max/0/color.jpg");
tester.testAuthorizationWhenUnauthorized(uri);
tester.testAuthorizationWhenUnauthorized(uri, getEndpointPath());
}

@Test
Expand Down
Loading