Skip to content

Commit edbe322

Browse files
seropianreschkerishabhdaimthomasmuellerbhabegger
authored
Oak 12219 - upgrade azure sdk v8 to v12 for oak blob azure rework (#3014)
* OAK-12287: Update to Apache Parent POM to version 39 * OAK-12287: Update to Apache Parent POM to version 39 - revert removal of bundle-plugin version * OAK-12289 : disabled blob id tracking for document node store (#2987) * OAK-12289 : disabled blob id tracking for document node store * OAK-12289 : fixed test case * OAK-12259: oak-http: OakServlet mis-parses HTTP Basic credentials * OAK-12293 : bump commons-io to 2.21.0 (#2997) * OAK-12293 : bump commons-io to 2.22.0 * OAK-12293 : bump commons-io to 2.21.0 * OAK-12294 : bump commons-codec to 1.20.0 (#2998) * OAK-12295 : bump mongo-driver-sync to 5.3.1 (#3001) * OAK-12295 : bump mongo-driver-sync to 5.3.1 * OAK-12295 : fixed the compilation issues * OAK-12296 : bump testcontainers version to 2.0.3 (#3002) * OAK-12296 : bump testcontainers version to 2.0.3 * OAK-12296 : fixed the junit 4 compatibility issues * OAK-12296 : fixed compatibulity issues with Junit 4 * OAK-12296 : fixed compatibulity issues with ES tests * OAK-12296 : incorporated review comments to use withEnv override for ElasticTestServer * OAK-12244: index nodes that gain a mixin rule, delete stale docs when… (#2949) * OAK-12244: index nodes that gain a mixin rule, delete stale docs when mixin rule is lost (#2938) When an existing node's applicable indexing rule changes at runtime (e.g. jcr:mixinTypes added or removed), FulltextIndexEditor did not update the index because propertiesChanged was never set — jcr:mixinTypes is not normally listed in a rule's property definitions. Track wasIndexable (rule matched before) alongside isIndexable() (rule matches after). In leave(), act on transitions: - !wasIndexable && isIndexable(): node gained a rule → addOrUpdate - wasIndexable && !isIndexable(): node lost a rule → deleteDocuments Tests added: - PropertyIndexCommonTest: two end-to-end integration tests (all backends) - LuceneIndexEditor2Test: two unit tests verifying writer.docs / writer.deletedPaths * OAK-12244: fix mixin type changes not reflected in fulltext index (#2953) Root cause: when a node gains or loses a mixin type at runtime, FulltextIndexEditor did not update the index because propertiesChanged was never set — jcr:mixinTypes is not normally listed in a rule's property definitions. Fix: track wasIndexable (rule matched before) alongside isIndexable() (rule matches after). In leave(), act on the indexing-rule transition: - !wasIndexable && isIndexable(): node gained a rule → addOrUpdate - wasIndexable && !isIndexable(): node lost a rule → deleteDocument Split FulltextIndexWriter into two explicit operations: - deleteDocumentTree(path): node physically removed; cascade is correct - deleteDocument(path): node lost indexability at runtime; exact only The original deleteDocuments used a PrefixQuery that cascaded to all descendants; in the mixin-loss branch this was a bug — children carrying their own mixin types were incorrectly evicted from the index. Additional changes: - Snapshot FT_OAK_12244_DISABLE once per commit cycle in FulltextIndexEditorContext as typeChangeTrackingEnabled so enter() and leave() always agree - Skip getApplicableIndexingRule(before) on the hot path via hasNodeTypeChange guard when neither jcr:primaryType nor jcr:mixinTypes changed - Register FT_OAK_12244 toggle in ElasticIndexProviderService - Reuse CommitFailedException code 5 for the deleteDocument error path Tests: - PropertyIndexCommonTest: end-to-end integration tests (all backends) - LuceneIndexEditor2Test: unit tests verifying writer.docs / writer.deletedPaths - Verified: 1245 tests, 0 failures in oak-lucene --------- Co-authored-by: Benjamin Habegger <bhabegger@adobe.com> * OAK-12282 : defining a fixed bound for the AbstractDiskCache (#2978) * OAK-12282 defining a fixed bound for the AbstractDiskCache --------- Co-authored-by: patlego <patriquelegault@gmail.com> * OAK-12303: Update mina-core dependency version to 2.1.15 (#3008) (#3010) ack @telegrapher Co-authored-by: Jose Antonio Insua <ungoliant@gmail.com> * Revert "OAK-12295 : bump mongo-driver-sync to 5.3.1 (#3001)" (#3012) This reverts commit 480eb14. * OAK-12219: Upgrade Azure SDK V8 to V12 for oak-blob-azure - rework Ai-Assisted-By: claude * OAK-12219: Upgrade Azure SDK V8 to V12 for oak-blob-azure - rework - Sonar fixes Ai-Assisted-By: claude * OAK-12219: Upgrade Azure SDK V8 to V12 for oak-blob-azure - rework - Sonar fixes Ai-Assisted-By: claude * OAK-12219: Upgrade Azure SDK V8 to V12 for oak-blob-azure - rework - pin jackson version to the one in AEM to prevent transitive dep download. Ai-Assisted-By: claude * OAK-12219: fix Sonar annotations from PR #2989 CI run AzureBlobStoreBackendV12: - RuntimeException -> IllegalStateException in getMetadataRecord/getAllMetadataRecords/deleteAllMetadataRecords - chain DataStoreException (not unwrapped IOException cause) in write() catch block - .collect(Collectors.toList()) -> .toList() in commitBlocksAndGetSize - instanceof pattern matching for BlobStorageException in completeHttpUpload - nested ternary -> if-else for operation string in presigned URI error log - return new byte[0] instead of null in readMetadataBytes; update caller check RegressionCSOV8Test: - swap assertEquals args to (actual, expected) order per Sonar S3415 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Ai-Assisted-By: claude-code * OAK-12219: raise unit-test coverage on new V12 code to clear Sonar gate PR #2989 failed the quality gate at 67.9% coverage on new code (need 80%). The Azure ITs that cover blob CRUD don't run in CI, so the new V12 classes were largely uncovered. Add mock-based unit tests (Mockito mocks the Azure BlobContainerClient/BlockBlobClient chain) so the SDK-call paths are covered without a live Azurite endpoint. New/expanded tests: - AzureBlobStoreBackendV12MockTest (new): read/getRecord/exists/deleteRecord success + 404 + storage-error paths; metadata get/exists/delete; init() config parsing (concurrent-request clamping, secondary location, presigned URI config); getAllIdentifiers/getAllRecords meta+no-dash filtering; getAllMetadataRecords success; write exists/missing/length-collision; addMetadataRecord(File); initiateHttpUpload arg validation; data record getters - AzureHttpRequestLoggingPolicyV12Test (new): verbose on/off process() paths - AzureDataStoreWrapperTest: createDataStore v8/v12, getDescription, init delegation, 3-arg upload, statistics provider getter/setter - AzureBlobContainerProviderV12Test: service-principal credential branch, getEndpointUrl variants Blended new-code coverage (line+branch) on the new files now ~82% locally. Also make getOrCreateReferenceKey tolerate a null from a readMetadataBytes override (production returns empty array; test subclasses may return null). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Ai-Assisted-By: claude * Oak 12219 upgrade azure sdk v8 to v12 for oak blob azure rework (#2992) * OAK-12219: Upgrade Azure SDK V8 to V12 for oak-blob-azure - rework - fix blob upload/download related constants that control memory and streaming behavior Ai-Assisted-By: claude * OAK-12219: Upgrade Azure SDK V8 to V12 for oak-blob-azure - rework - fix blob block size, prevent size zero Ai-Assisted-By: claude * OAK-12219: Upgrade Azure SDK V8 to V12 for oak-blob-azure - rework - removed oak-run-commons dependency on azure sdk v8 from oak-blob-cloud-azure Ai-Assisted-By: claude * OAK-12219: Upgrade Azure SDK V8 to V12 for oak-blob-azure - rework - cached the UserDelegationKey to avoid repeated calls to get it from Azure for every URI. Ai-Assisted-By: claude * Oak 12219 upgrade azure sdk v8 to v12 for oak blob azure rework (#2994) * OAK-12219: Upgrade Azure SDK V8 to V12 for oak-blob-azure - rework - fix blob upload/download related constants that control memory and streaming behavior Ai-Assisted-By: claude Ai-Assisted-By: claude-code * OAK-12219: Upgrade Azure SDK V8 to V12 for oak-blob-azure - rework - fix blob block size, prevent size zero Ai-Assisted-By: claude Ai-Assisted-By: claude-code * OAK-12219: Upgrade Azure SDK V8 to V12 for oak-blob-azure - rework - fixed automated code review findings Ai-Assisted-By: claude-code * OAK-12219: Upgrade Azure SDK V8 to V12 for oak-blob-azure - rework - PropertiesUtil.populate() uses reflection to inject config properties (e.g. cacheSize) into the DataStore returned by createDataStore(). When the class is package-private, Java's reflection access control blocks it because the caller is in a different package — even if the individual setter methods are public. Making the class itself public fixes that Ai-Assisted-By: claude Ai-Assisted-By: claude-code * OAK-12219: Upgrade Azure SDK V8 to V12 for oak-blob-azure - rework - fix blob upload/download related constants that control memory and streaming behavior Ai-Assisted-By: claude Ai-Assisted-By: claude-code * OAK-12219: Upgrade Azure SDK V8 to V12 for oak-blob-azure - rework - fix blob block size, prevent size zero Ai-Assisted-By: claude Ai-Assisted-By: claude-code * Remove internal service reference from comment Drop 'e.g. oak-repository-service' — internal detail, not relevant to Apache OSS. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Ai-Assisted-By: claude-code * OAK-12219: cache HttpClient in AzureBlobContainerProviderV12 Build the Netty HTTP client once at construction instead of on every getBlobContainer() call. Also drops dead Properties threading and duplicate proxy/connection-string overloads in UtilsV12. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Ai-Assisted-By: claude-code * OAK-12219: replace wildcard imports with explicit imports Ai-Assisted-By: claude-code * OAK-12219: move retryOptions into provider, drop getBlobContainerFromServicePrincipals retryOptions is now a field built at construction instead of passed per-call. Service-principal auth reuses the cached BlobServiceClient instead of rebuilding a client on every call. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Ai-Assisted-By: claude-code * OAK-12219: address remaining PR #2982 review comments Use SystemPropertySupplier for the JVM-property override (reschke), rename registerService to registerDataStoreService (joerghoh), and match the suggested "falling back to v8" log wording. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Ai-Assisted-By: claude,claude-code Ai-Assisted-By: claude-code * OAK-12219: Upgrade Azure SDK V8 to V12 for oak-blob-azure - rework - fixed comments from #2982 * OAK-12219: Upgrade Azure SDK V8 to V12 for oak-blob-azure - rework - fixed comments from #2989 --------- Co-authored-by: Julian Reschke <julian.reschke@gmx.de> Co-authored-by: Rishabh Kumar <rishabhdaim1991@gmail.com> Co-authored-by: Julian Reschke <reschke@apache.org> Co-authored-by: Thomas Mueller <thomasm@apache.org> Co-authored-by: Benjamin Habegger <bhabegger@adobe.com> Co-authored-by: Patrique Legault <patrique.legault@gmail.com> Co-authored-by: patlego <patriquelegault@gmail.com> Co-authored-by: Jose Antonio Insua <ungoliant@gmail.com>
1 parent 7c57aad commit edbe322

46 files changed

Lines changed: 670 additions & 144 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,4 @@ atlassian-ide-plugin.xml
1212
derby.log
1313
.java-version
1414
oak-shaded-guava/dependency-reduced-pom.xml
15-
.DS_Store
16-
.claude/settings.local.json
1715
pom.xml.versionsBackup

oak-auth-ldap/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@
9494
<dependency>
9595
<groupId>org.apache.mina</groupId>
9696
<artifactId>mina-core</artifactId>
97-
<version>2.1.12</version>
97+
<version>2.1.15</version>
9898
</dependency>
9999
<dependency>
100100
<groupId>org.apache.servicemix.bundles</groupId>

oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AbstractAzureDataStoreService.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@
3333
import org.osgi.service.component.ComponentContext;
3434

3535
/**
36+
* @deprecated Use {@link AzureDataStoreWrapper} instead.
37+
*/
38+
@Deprecated(since = "2.3", forRemoval = true)
3639
* Kept for binary compatibility with existing callers. Use {@link AzureDataStoreWrapper} instead.
3740
*
3841
* @deprecated

oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureDataStoreWrapper.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ public class AzureDataStoreWrapper extends AbstractDataStoreService {
7878

7979
private static final Logger log = LoggerFactory.getLogger(AzureDataStoreWrapper.class);
8080

81+
// Intentionally set to the legacy v8 PID rather than this class's own FQN.
82+
// Existing OSGi configurations reference the v8 PID, so reusing it here means
83+
// no config migration is required when switching to this wrapper.
8184
public static final String NAME = "org.apache.jackrabbit.oak.plugins.blob.datastore.AzureDataStore";
8285

8386
// Same name for now; kept as separate constants so they can diverge if the sources need different keys later.
@@ -189,7 +192,8 @@ protected void setStatisticsProvider(StatisticsProvider statisticsProvider) {
189192

190193
@Override
191194
protected String[] getDescription() {
192-
return new String[]{"type=AzureBlob"};
195+
String sdkVersion = (activeImpl instanceof AzureDataStoreV12) ? "v12" : "v8";
196+
return new String[]{"type=AzureBlob", "sdkVersion=" + sdkVersion};
193197
}
194198

195199
// -- Inner DelegatingDataStore (returned from createDataStore) -------

oak-doc/src/site/markdown/osgi_config.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,9 @@ blobTrackSnapshotIntervalInSecs (long) - 0
229229
deletions will be visible to other cluster nodes or repositories connected to the shared DatStore after this. This
230230
should be less than the blobGcMaxAgeInSecs parameter above and the frequency of blob gc. See [Blob
231231
tracker][blobtracker].
232-
A value of `0` (the default since Oak 2.4.0) disables blob ID tracking entirely for the Segment Node Store. To enable tracking,
233-
set this to a positive value (e.g. `43200` for 12 hours). Note that DocumentNodeStore defaults to `43200`.
232+
A value of `0` disables blob ID tracking entirely. This has been the default for both SegmentNodeStore and
233+
DocumentNodeStore since Oak 2.4.0.
234+
To enable tracking, set this to a positive value (e.g. `43200` for 12 hours).
234235

235236
<a name="document-node-store"></a>
236237
#### DocumentNodeStore
@@ -262,7 +263,7 @@ docChildrenCachePercentage | 0 (was 3 until 1.5.6) | Percentage of `cache` alloc
262263
cacheSegmentCount | 16 | The number of segments in the LIRS cache | 1.0.15, 1.2.3, 1.3.0
263264
cacheStackMoveDistance | 16 | The delay to move entries to the head of the queue in the LIRS cache | 1.0.15, 1.2.3, 1.3.0
264265
sharedDSRepoId | "" | Custom SharedDataStore repositoryId. Used when custom blobstore configured. Should be unique among the repositories sharing the datastore. | 1.2.11
265-
blobTrackSnapshotIntervalInSecs | 43200 (12 hrs) | The blob ids cached/tracked locally are synchronized with the DataStore at this interval. Any additions and deletions will be visible to other cluster nodes or repositories connected to the shared DatStore after this. This should be less than the blobGcMaxAgeInSecs parameter above and the frequency of blob gc. See [Blob tracker][blobtracker]. | 1.5.6
266+
blobTrackSnapshotIntervalInSecs | 0 | The blob ids cached/tracked locally are synchronized with the DataStore at this interval. Any additions and deletions will be visible to other cluster nodes or repositories connected to the shared DatStore after this. This should be less than the blobGcMaxAgeInSecs parameter above and the frequency of blob gc. A value of `0` disables blob ID tracking (default since Oak 2.4.0). See [Blob tracker][blobtracker]. | 1.5.6
266267
updateLimit | 100000 | The number of updates kept in memory until changes are written to a branch in the DocumentStore | 1.7.0
267268
leaseCheckMode | STRICT | The lease check mode. `STRICT` is the default and will stop the DocumentNodeStore as soon as the lease expires. `LENIENT` will give the background lease update a chance to renew the lease even when the lease expired. This mode is only recommended for development, e.g. when debugging an application and the lease may expire when the JVM is stopped at a breakpoint. | 1.9.6
268269
documentStoreType | MONGO | Set to "RDB" for `RDBDocumentStore`; will require a configured Sling DataSource called `oak`. | 1.0

oak-doc/src/site/markdown/plugins/blobstore.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -237,9 +237,11 @@ configured NodeStore services.
237237
**Default behavior by NodeStore:**
238238

239239
* **SegmentNodeStore** (`SegmentNodeStoreService` / `SegmentNodeStoreFactory`): blob ID tracking is **disabled by
240-
default** (`blobTrackSnapshotIntervalInSecs = 0`). To enable it, set a positive value such as `43200` (12 hours).
241-
* **DocumentNodeStore**: blob ID tracking is **enabled by default** with a 12-hour sync interval
242-
(`blobTrackSnapshotIntervalInSecs = 43200`).
240+
default** (`blobTrackSnapshotIntervalInSecs = 0`, default since Oak 2.4.0). To enable it, set a positive value such as
241+
`43200` (12 hours).
242+
* **DocumentNodeStore**: blob ID tracking is **disabled by default**
243+
(`blobTrackSnapshotIntervalInSecs = 0`, default since Oak 2.4.0). To enable it, set a positive value such as `43200`
244+
(12 hours).
243245

244246
When tracking is disabled (interval = 0), blob IDs are not cached locally and the warnings described below do
245247
not apply. The full blob ID list is always retrieved directly from the DataStore during GC.

oak-http/src/main/java/org/apache/jackrabbit/oak/http/AuthorizationField.java

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@
1616
*/
1717
package org.apache.jackrabbit.oak.http;
1818

19-
import org.apache.jackrabbit.util.Base64;
20-
2119
import javax.jcr.SimpleCredentials;
2220
import javax.security.auth.login.LoginException;
21+
import java.nio.charset.StandardCharsets;
22+
import java.util.Base64;
2323
import java.util.Enumeration;
24+
import java.util.Locale;
2425
import java.util.NoSuchElementException;
2526

2627
public class AuthorizationField {
@@ -44,11 +45,30 @@ public static SimpleCredentials valueOf(Enumeration<String> values) throws Login
4445
return parseCredentials(field);
4546
}
4647

47-
private static SimpleCredentials parseCredentials(String fieldValue) throws LoginException {
48-
if (fieldValue.startsWith("Basic ")) {
49-
String[] basic =
50-
Base64.decode(fieldValue.substring("Basic ".length())).split(":");
51-
return new SimpleCredentials(basic[0], basic[1].toCharArray());
48+
private static SimpleCredentials parseCredentials(String rawFieldValue) throws LoginException {
49+
boolean hasControls = rawFieldValue.chars().anyMatch(c -> c < ' ');
50+
if (hasControls) {
51+
throw new LoginException("Control characters are not allowed");
52+
}
53+
54+
String fieldValue = rawFieldValue.trim().replaceAll(" +", " ");
55+
56+
if (fieldValue.toLowerCase(Locale.ENGLISH).startsWith("basic ")) {
57+
String token68 = fieldValue.substring("basic ".length());
58+
try {
59+
String decoded = new String(Base64.getDecoder().decode(token68), StandardCharsets.UTF_8);
60+
int colon = decoded.indexOf(':');
61+
if (colon < 0) {
62+
throw new LoginException(
63+
"Malformed Basic credentials: missing ':' separator");
64+
}
65+
String userId = decoded.substring(0, colon);
66+
String password = decoded.substring(colon + 1);
67+
68+
return new SimpleCredentials(userId, password.toCharArray());
69+
} catch (IllegalArgumentException ex) {
70+
throw new LoginException(ex.getMessage());
71+
}
5272
} else {
5373
throw new LoginException("Only Basic Authentication supported");
5474
}

oak-http/src/test/java/org/apache/jackrabbit/oak/http/AuthorizationFieldTest.java

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,10 @@ public void testValid() throws LoginException {
4747
assertEquals("bar", new String(credentials.getPassword()));
4848
}
4949

50-
@Test(expected = ArrayIndexOutOfBoundsException.class) // BUG: OAK-12259
50+
@Test(expected = LoginException.class)
5151
public void testInvalidBase64() throws LoginException {
5252
String b64 = Base64.getEncoder().encodeToString("foo:bar".getBytes(StandardCharsets.UTF_8));
53-
SimpleCredentials credentials = AuthorizationField.valueOf(Collections.enumeration(List.of("Basic dksjdkj" + b64)));
54-
assertEquals("foo", credentials.getUserID());
55-
assertEquals("bar", new String(credentials.getPassword()));
53+
AuthorizationField.valueOf(Collections.enumeration(List.of("Basic dksjdkj" + b64)));
5654
}
5755

5856
@Test(expected = LoginException.class)
@@ -64,9 +62,67 @@ public void testNoScheme() throws LoginException {
6462
@Test
6563
public void testColonInPassword() throws LoginException {
6664
String b64 = "Basic " + Base64.getEncoder().encodeToString("foo:bar:qux".getBytes(StandardCharsets.UTF_8));
67-
SimpleCredentials credentials =AuthorizationField.valueOf(Collections.enumeration(List.of(b64)));
65+
SimpleCredentials credentials = AuthorizationField.valueOf(Collections.enumeration(List.of(b64)));
66+
assertEquals("foo", credentials.getUserID());
67+
assertEquals("bar:qux", new String(credentials.getPassword()));
68+
}
69+
70+
@Test(expected = LoginException.class)
71+
public void testMissingColon() throws LoginException {
72+
String b64 = "Basic " + Base64.getEncoder().encodeToString("foobarqux".getBytes(StandardCharsets.UTF_8));
73+
AuthorizationField.valueOf(Collections.enumeration(List.of(b64)));
74+
}
75+
76+
@Test
77+
public void testSchemeCase() throws LoginException {
78+
String b64 = Base64.getEncoder().encodeToString("foo:bar".getBytes(StandardCharsets.UTF_8));
79+
SimpleCredentials credentials = AuthorizationField.valueOf(Collections.enumeration(List.of("BaSiC " + b64)));
80+
assertEquals("foo", credentials.getUserID());
81+
assertEquals("bar", new String(credentials.getPassword()));
82+
}
83+
84+
@Test
85+
public void testMoreWhitespace() throws LoginException {
86+
String b64 = Base64.getEncoder().encodeToString("foo:bar".getBytes(StandardCharsets.UTF_8));
87+
SimpleCredentials credentials = AuthorizationField.valueOf(Collections.enumeration(List.of("Basic " + b64)));
88+
assertEquals("foo", credentials.getUserID());
89+
assertEquals("bar", new String(credentials.getPassword()));
90+
}
91+
92+
@Test(expected = LoginException.class)
93+
public void testNonSpWhitespace() throws LoginException {
94+
String b64 = Base64.getEncoder().encodeToString("foo:bar".getBytes(StandardCharsets.UTF_8));
95+
AuthorizationField.valueOf(Collections.enumeration(List.of("Basic \t " + b64)));
96+
}
97+
98+
@Test(expected = LoginException.class)
99+
public void testBrokenBase64() throws LoginException {
100+
String b64 = Base64.getEncoder().encodeToString("foo:bar".getBytes(StandardCharsets.UTF_8));
101+
// insert a single SP into the base64 sequence
102+
b64 = b64.substring(0,5) + " " + b64.substring(5);
103+
AuthorizationField.valueOf(Collections.enumeration(List.of("Basic " + b64)));
104+
}
105+
106+
@Test(expected = LoginException.class)
107+
public void testMoreBrokenBase64() throws LoginException {
108+
String b64 = Base64.getEncoder().encodeToString("foo:bar".getBytes(StandardCharsets.UTF_8));
109+
b64 = b64.substring(0,5) + "=" + b64.substring(5);
110+
AuthorizationField.valueOf(Collections.enumeration(List.of("Basic " + b64)));
111+
}
112+
113+
@Test
114+
public void testMoreNonAscii() throws LoginException {
115+
String b64 = Base64.getEncoder().encodeToString("test:123\u00a3".getBytes(StandardCharsets.UTF_8));
116+
SimpleCredentials credentials = AuthorizationField.valueOf(Collections.enumeration(List.of("Basic " + b64)));
117+
assertEquals("test", credentials.getUserID());
118+
assertEquals("123\u00a3", new String(credentials.getPassword()));
119+
}
120+
121+
@Test
122+
public void testBasic64NoPadding() throws LoginException {
123+
String b64 = Base64.getEncoder().withoutPadding().encodeToString("foo:bar".getBytes(StandardCharsets.UTF_8));
124+
SimpleCredentials credentials = AuthorizationField.valueOf(Collections.enumeration(List.of("Basic " + b64)));
68125
assertEquals("foo", credentials.getUserID());
69-
// BUG: OAK-12259
70126
assertEquals("bar", new String(credentials.getPassword()));
71127
}
72128
}

oak-it-osgi/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@
198198
</dependency>
199199
<dependency>
200200
<groupId>org.testcontainers</groupId>
201-
<artifactId>elasticsearch</artifactId>
201+
<artifactId>testcontainers-elasticsearch</artifactId>
202202
<version>${testcontainers.version}</version>
203203
<scope>test</scope>
204204
</dependency>

oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/blob/DocumentBlobTrackerRegistrationTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ protected void registerNodeStoreService() {
6363
properties.put("repository.home", repoHome);
6464
properties.put("mongouri", MongoUtils.URL);
6565
properties.put("db", MongoUtils.DB);
66+
// Explicitly enable blob ID tracking (default is 0/disabled since Oak 2.4.0).
67+
// This test verifies tracker reinitialization which requires an active tracker.
68+
properties.put("blobTrackSnapshotIntervalInSecs", 3600L);
6669
MockOsgi.setConfigForPid(context.bundleContext(),
6770
DocumentNodeStoreService.class.getName(), properties);
6871
service = context.registerInjectActivateService(new DocumentNodeStoreService());

0 commit comments

Comments
 (0)