Skip to content

Commit 8b53da4

Browse files
committed
Updates for testing on test env
1 parent a9264d6 commit 8b53da4

File tree

6 files changed

+73
-62
lines changed

6 files changed

+73
-62
lines changed

.github/workflows/build.yml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,6 @@ jobs:
1919
java: [ 17 ]
2020

2121
steps:
22-
- name: Set up build cache
23-
uses: actions/cache@0781355a23dac32fd3bac414512f4b903437991a # v2
24-
if: ${{ env.MAVEN_CACHE_KEY }}
25-
with:
26-
path: |
27-
~/.m2
28-
lib
29-
key: uclalibrary-cache-${{ env.MAVEN_CACHE_KEY }}-${{ hashFiles('**/pom.xml') }}
30-
restore-keys: uclalibrary-cache-${{ env.MAVEN_CACHE_KEY }}-
3122
- name: Check out code
3223
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2
3324
with:

pom.xml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,11 @@
6969
<docker.maven.plugin.version>0.45.1</docker.maven.plugin.version>
7070

7171
<!-- Docker images versions used in testing -->
72-
<hauth.container.version>0.0.7</hauth.container.version>
72+
<hauth.container.version>1.0.7</hauth.container.version>
7373
<psql.container.version>12.7-alpine</psql.container.version>
7474
<redis.container.version>6.2.5-alpine</redis.container.version>
75-
<cantaloupe.container.version>5.0.6</cantaloupe.container.version>
75+
<!--<cantaloupe.container.version>5.0.6-6</cantaloupe.container.version>-->
76+
<cantaloupe.container.version>5.0.6-4</cantaloupe.container.version>
7677

7778
<!-- Build-time options -->
7879
<update.sql>false</update.sql>

src/main/java/edu/ucla/library/iiif/auth/delegate/HauthDelegate.java

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import info.freelibrary.util.HTTP;
2323
import info.freelibrary.util.Logger;
2424
import info.freelibrary.util.LoggerFactory;
25+
2526
import info.freelibrary.iiif.presentation.v3.services.AuthCookieService1;
2627
import info.freelibrary.iiif.presentation.v3.services.AuthTokenService1;
2728
import info.freelibrary.iiif.presentation.v3.services.ExternalCookieService1;
@@ -33,6 +34,7 @@
3334
import edu.ucla.library.iiif.auth.delegate.hauth.HauthItem;
3435
import edu.ucla.library.iiif.auth.delegate.hauth.HauthSinaiToken;
3536
import edu.ucla.library.iiif.auth.delegate.hauth.HauthToken;
37+
3638
import edu.illinois.library.cantaloupe.delegate.JavaContext;
3739
import edu.illinois.library.cantaloupe.delegate.JavaDelegate;
3840

@@ -52,9 +54,9 @@ public class HauthDelegate extends CantaloupeDelegate implements JavaDelegate {
5254
private static final TypeReference<Map<String, Object>> MAP_TYPE_REFERENCE = new TypeReference<>() {};
5355

5456
/**
55-
* The default thumbnail dimensions.
57+
* The way we identify if a thumbnail is being requested.
5658
*/
57-
private static final String THUMBNAIL_DIMS = "/!200,200/";
59+
private static final String THUMBNAIL = "/full/!200,200/0/";
5860

5961
/**
6062
* The name of the Cookie HTTP request header.
@@ -135,8 +137,9 @@ public Object preAuthorize() {
135137
final JavaContext context = getContext();
136138
final String id = context.getIdentifier();
137139

138-
// We let all thumbnail requests through regardless of authorization
139-
if (context.getLocalURI().contains(THUMBNAIL_DIMS)) {
140+
// Allow any kind of thumbnail request, no questions asked
141+
if (context.getLocalURI().contains(THUMBNAIL)) {
142+
LOGGER.debug(MessageCodes.CAD_030, context.getLocalURI());
140143
return true;
141144
}
142145

@@ -202,18 +205,21 @@ private Object getTieredInfo() {
202205
return true;
203206
}
204207

205-
// Degraded image request for the size we allow (probably via an earlier HTTP 302 redirect)
208+
// Degraded image request for the size we allow (probably via an earlier HTTP
209+
// 302 redirect)
206210
if (Arrays.equals(configuredScaleConstraint, scaleConstraint)) {
207211
return myInfoJsonShouldContainAuth = true;
208212
}
209213

210-
// Degraded image request for a size that doesn't match what we've configured and isn't 1:1
214+
// Degraded image request for a size that doesn't match what we've configured
215+
// and isn't 1:1
211216
if (scaleConstraint[0] != scaleConstraint[1]) {
212217
LOGGER.debug(MessageCodes.CAD_015, scaleConstraint[0], scaleConstraint[1]);
213218
return false; // returns 403
214219
}
215220

216-
// Full image request, but non-campus IP (the long types make a difference here, apparently)
221+
// Full image request, but non-campus IP (the long types make a difference here,
222+
// apparently)
217223
LOGGER.debug(MessageCodes.CAD_016);
218224
return Map.of(STATUS_CODE, Long.valueOf(HTTP.FOUND), //
219225
SCALE_NUMERATOR, (long) configuredScaleConstraint[0], //
@@ -232,7 +238,8 @@ private Object getTieredImage() {
232238

233239
LOGGER.debug(MessageCodes.CAD_017);
234240

235-
// Degraded image request for the size we allow (probably via an earlier HTTP 302 redirect)
241+
// Degraded image request for the size we allow (probably via an earlier HTTP
242+
// 302 redirect)
236243
if (Arrays.equals(configuredScaleConstraint, scaleConstraint)) {
237244
LOGGER.debug(MessageCodes.CAD_027);
238245
return true;
@@ -250,7 +257,8 @@ private Object getTieredImage() {
250257
return true;
251258
}
252259

253-
// Full image request, but non-campus IP (the long types make a difference here, apparently)
260+
// Full image request, but non-campus IP (the long types make a difference here,
261+
// apparently)
254262
LOGGER.debug(MessageCodes.CAD_019);
255263
return Map.of(STATUS_CODE, Long.valueOf(HTTP.FOUND), //
256264
SCALE_NUMERATOR, (long) configuredScaleConstraint[0], //
@@ -324,11 +332,13 @@ private Map<String, Object> getAuthServices() {
324332
break;
325333
case OPEN:
326334
default:
327-
// The OPEN and default branches should not be reachable, but are included here just in case
335+
// The OPEN and default branches should not be reachable, but are included here
336+
// just in case
328337
return Collections.emptyMap();
329338
}
330339

331-
// Workaround for Mirador bug that requires label be present (Cf. https://bitly.com/3NllMLq+)
340+
// Workaround for Mirador bug that requires label be present (Cf.
341+
// https://bitly.com/3NllMLq+)
332342
serviceMap = JSON.convertValue(cookieService, MAP_TYPE_REFERENCE);
333343
serviceMap.putIfAbsent(JsonKeys.LABEL, label);
334344

src/main/resources/auth-delegate_messages.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,6 @@
3535
<entry key="CAD-027">Access allowed: Degraded image request for the size we allow</entry>
3636
<entry key="CAD-028">Access denied: Degraded image request for a size we don't allow: {}:{}</entry>
3737
<entry key="CAD-029">Request header "{}" not found</entry>
38+
<entry key="CAD-030">Letting a thumbnail request through: {}</entry>
3839

3940
</properties>

src/test/java/edu/ucla/library/iiif/auth/delegate/HauthDelegateIT.java

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@
3737
*/
3838
public class HauthDelegateIT {
3939

40+
/**
41+
* A sample thumbnail IIIF request.
42+
*/
43+
private static final String THUMBNAIL = "/full/!200,200/0/default.tif";
44+
4045
/**
4146
* The template for image URLs. The slots are:
4247
* <ul>
@@ -54,7 +59,7 @@ public class HauthDelegateIT {
5459
* </code>
5560
* <p>
5661
* This would be the value of the "accessToken" key shown
57-
* <a href="https://iiif.io/api/auth/1.0/#the-json-access-token-response">here</a>.
62+
* <a href= "https://iiif.io/api/auth/1.0/#the-json-access-token-response">here</a>.
5863
*/
5964
private static final String ACCESS_TOKEN =
6065
"eyJ2ZXJzaW9uIjogIjAuMC4wLVNOQVBTSE9UIiwgImNhbXB1c05ldHdvcmsiOiB0cnVlfQo=";
@@ -66,39 +71,11 @@ public class HauthDelegateIT {
6671
* </code>
6772
* <p>
6873
* This would be the value of the "accessToken" key shown
69-
* <a href="https://iiif.io/api/auth/1.0/#the-json-access-token-response">here</a>.
74+
* <a href= "https://iiif.io/api/auth/1.0/#the-json-access-token-response">here</a>.
7075
*/
7176
private static final String SINAI_ACCESS_TOKEN =
7277
"eyJ2ZXJzaW9uIjogIjAuMC4wLVNOQVBTSE9UIiwgInNpbmFpQWZmaWxpYXRlIjogdHJ1ZX0K";
7378

74-
/**
75-
* A test initialization vector used to encrypt {@link #TEST_SINAI_AUTHENTICATED_3DAY}. This is just the value
76-
* "0123456789ABCDEF" (see Ruby script below) encoded in hexadecimal.
77-
*/
78-
private static final String TEST_INITIALIZATION_VECTOR = "30313233343536373839414243444546";
79-
80-
/**
81-
* A test cookie generated using the following Ruby code, mocking the relevant part of the Sinai application.
82-
* <p>
83-
*
84-
* <pre>
85-
* #!/usr/bin/env ruby
86-
*
87-
* require "openssl"
88-
*
89-
* cipher = OpenSSL::Cipher::AES256.new :CBC
90-
* cipher.encrypt
91-
* cipher.key = "ThisPasswordIsReallyHardToGuess!"
92-
* cipher.iv = "0123456789ABCDEF"
93-
* puts (cipher.update("Authenticated #{Time.at(0).utc}") + cipher.final).unpack("H*")[0].upcase
94-
* </pre>
95-
*
96-
* @see <a href= "https://github.com/UCLALibrary/sinaimanuscripts/blob/44cbbd9bf508c32b742f1617205a679edf77603e/app/
97-
* controllers/application_controller.rb#L98-L103">How the Sinai application encodes cookies</a>
98-
*/
99-
private static final String TEST_SINAI_AUTHENTICATED_3DAY =
100-
"5AFF80488740353F8A11B99C7A493D871807521908500772B92E4F8FC919E305A607ADB714B22EF08D2C22FC08C8A6EC";
101-
10279
/**
10380
* The id of the non-restricted image.
10481
*/
@@ -183,12 +160,38 @@ public class HauthDelegateIT {
183160

184161
/**
185162
* Tests that thumbnails of access controlled items are still displayed.
163+
*
164+
* @throws InterruptedException If the test is interrupted
165+
* @throws IOException If there is trouble reading and writing test resources
186166
*/
187167
@Test
188-
public final void testAccessControlledThumbnails() throws InterruptedException, IOException {
189-
final String imageURL =
190-
StringUtils.format(IMAGE_URL_TEMPLATE, System.getenv().get(TestConfig.IIIF_URL_PROPERTY), 2,
191-
ALL_OR_NOTHING_ACCESS_IMAGE + "/full/!200,200/0/default.tif");
168+
public final void testAllOrNothingThumbnails() throws InterruptedException, IOException {
169+
final String imageURL = StringUtils.format(IMAGE_URL_TEMPLATE,
170+
System.getenv().get(TestConfig.IIIF_URL_PROPERTY), 2, ALL_OR_NOTHING_ACCESS_IMAGE + THUMBNAIL);
171+
final HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(URI.create(imageURL));
172+
final HttpResponse<byte[]> response = HTTP_CLIENT.send(requestBuilder.build(), BodyHandlers.ofByteArray());
173+
final ByteArrayInputStream byteArrayInputStream;
174+
final BufferedImage image;
175+
176+
assertEquals(200, response.statusCode());
177+
178+
byteArrayInputStream = new ByteArrayInputStream(response.body());
179+
image = ImageIO.read(byteArrayInputStream);
180+
181+
assertEquals(200, image.getHeight());
182+
assertEquals(200, image.getWidth());
183+
}
184+
185+
/**
186+
* Tests that thumbnails of access controlled items are still displayed.
187+
*
188+
* @throws InterruptedException If the test is interrupted
189+
* @throws IOException If there is trouble reading and writing test resources
190+
*/
191+
@Test
192+
public final void testTieredAccessControlledThumbnails() throws InterruptedException, IOException {
193+
final String imageURL = StringUtils.format(IMAGE_URL_TEMPLATE,
194+
System.getenv().get(TestConfig.IIIF_URL_PROPERTY), 2, TIERED_ACCESS_IMAGE_DEGRADED_VALID + THUMBNAIL);
192195
final HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(URI.create(imageURL));
193196
final HttpResponse<byte[]> response = HTTP_CLIENT.send(requestBuilder.build(), BodyHandlers.ofByteArray());
194197
final ByteArrayInputStream byteArrayInputStream;
@@ -350,7 +353,8 @@ private static String getImageURL(final String aBaseURL, final int aImageApiVers
350353
final String imageApiPathTemplate;
351354
final String imageApiPath;
352355

353-
// Use TIFFs so that we can easily compare the response payload with the source image
356+
// Use TIFFs so that we can easily compare the response payload with the source
357+
// image
354358
switch (aImageApiVersion) {
355359
case 2:
356360
imageApiPathTemplate = "{}/full/full/0/default.tif";
@@ -481,6 +485,8 @@ public final void testErrorResponseTieredDisallowedScale() throws IOException, I
481485
final HttpResponse<String> response =
482486
sendImageInfoRequest(TIERED_ACCESS_IMAGE_DEGRADED_UNAVAILABLE, null, 2);
483487

488+
System.out.println(response.headers().toString());
489+
484490
assertEquals(HTTP.FORBIDDEN, response.statusCode());
485491
assertFalse(TestUtils.responseHasContentType(response, MediaType.APPLICATION_JSON,
486492
MediaType.APPLICATION_LD_PLUS_JSON));
@@ -506,6 +512,9 @@ public final void testNoAccessResponseAllOrNothingUnauthorized() throws IOExcept
506512
getExpectedImageInfo(ALL_OR_NOTHING_ACCESS_IMAGE, NO_ACCESS_RESPONSE_TEMPLATE_V2, 2);
507513

508514
assertEquals(HTTP.UNAUTHORIZED, response.statusCode());
515+
516+
System.out.println(response.headers().toString());
517+
509518
assertTrue(TestUtils.responseHasContentType(response, MediaType.APPLICATION_JSON,
510519
MediaType.APPLICATION_LD_PLUS_JSON));
511520
TestUtils.assertEquals(expectedResponse, response.body());

src/test/java/edu/ucla/library/iiif/auth/delegate/TestUtils.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.util.Collections;
1616
import java.util.Comparator;
1717
import java.util.List;
18+
import java.util.Optional;
1819
import java.util.Random;
1920
import java.util.TreeMap;
2021
import java.util.stream.Stream;
@@ -112,11 +113,9 @@ public static String[] getMockSinaiCookieValues() throws IOException {
112113
* @return Whether or not at least one of the content types is included in the response's Content-Type header
113114
*/
114115
public static boolean responseHasContentType(final HttpResponse<?> aResponse, final MediaType... aMediaTypes) {
115-
final String contentTypeHeader = aResponse.headers().firstValue(HttpHeaders.CONTENT_TYPE).get();
116-
117-
return Stream.of(aMediaTypes).map(String::valueOf).anyMatch(value -> {
118-
return contentTypeHeader.contains(value);
119-
});
116+
final Optional<String> contentType = aResponse.headers().firstValue(HttpHeaders.CONTENT_TYPE);
117+
return contentType.isPresent() &&
118+
Stream.of(aMediaTypes).map(String::valueOf).anyMatch(value -> contentType.get().contains(value));
120119
}
121120

122121
/**

0 commit comments

Comments
 (0)