Skip to content

Commit feb3eb2

Browse files
committed
[DO NOT MERGE] Core, REST, OAuth2: AuthManager v2 (MVP)
**Do not review this PR unless you are curious to get an early preview of Auth Manager v2 :-)** This PR encompasses the full scope of the AuthManager v2 effort, except support for human-to-machine grants (Authorization Code and Device Code), which will be introduced in a subsequent update. Cf. Auth Manager v2 [design document]. The goal is to provide complete overview of the planned changes for the curious reader. This PR is not meant to be merged as-is: smaller, incremental PRs will be opened later to introduce these changes gradually. **Key features of the new implementation:** - **Standards-compliant OAuth2/OIDC support** with proper client authentication methods (`client_secret_basic`, `client_secret_post`, `none`) - **OpenID Connect Discovery** for automatic endpoint resolution via `issuer-url` - **Token Exchange** support (RFC 8693) and **Refresh Token** flows - **Custom token endpoint parameters** (e.g. Auth0 `audience` via `rest.auth.oauth2.extra-params.*`) - **Automatic background token refresh** - **Automatic migration** of legacy property names with deprecation warnings at runtime **Architecture:** - `oauth2/` — Core classes: `OAuth2Manager`, `OAuth2Session`, `OAuth2Runtime`, `OAuth2Config` - `oauth2/config/` — Configuration model: `BasicConfig`, `TokenExchangeConfig`, `TokenRefreshConfig`, `ConfigMigrator`, `ConfigValidator` - `oauth2/flow/` — OAuth2 grant flows: `ClientCredentialsFlow`, `RefreshTokenFlow`, `TokenExchangeFlow`, `EndpointProvider` - `oauth2/client/` — Low-level `OAuth2Client` for HTTP token requests - `oauth2/http/` — `RESTClientAdapter` bridging Iceberg's `RESTClient` to the OAuth2 client **Deprecations:** - `org.apache.iceberg.rest.auth.OAuth2Manager` — deprecated, removal planned for 1.14.0 - `org.apache.iceberg.rest.auth.OAuth2Properties` — deprecated in favor of `OAuth2Config` - `org.apache.iceberg.rest.auth.OAuth2Util` — deprecated in favor of the new `OAuth2Manager` Other deprecations affect the REST layer (error handlers, etc.). **Docs:** Adds an OAuth2 configuration reference page (auto-generated from code) and a migration guide. **Tests:** ~11,000 lines of new tests including unit tests for all components with MockServer, and Keycloak-based integration tests. [design document]:https://docs.google.com/document/d/1Hxw-t8Maa7wZFmrlSujm7LRawKsFP3Q31tET_3aRnQU/edit
1 parent 69caca0 commit feb3eb2

78 files changed

Lines changed: 11030 additions & 11 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.

.github/workflows/java-ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ jobs:
109109
with:
110110
distribution: zulu
111111
java-version: ${{ matrix.jvm }}
112-
- run: ./gradlew -DallModules build -x test -x javadoc -x integrationTest
112+
- run: ./gradlew -DallModules build -x test -x javadoc -x integrationTest -x intTest
113113

114114
build-javadoc:
115115
runs-on: ubuntu-24.04

build.gradle

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,9 +357,43 @@ project(':iceberg-common') {
357357
}
358358

359359
project(':iceberg-core') {
360+
apply plugin: 'java-test-fixtures'
361+
apply plugin: 'jvm-test-suite'
362+
360363
test {
361364
useJUnitPlatform()
362365
}
366+
367+
testing {
368+
suites {
369+
intTest(JvmTestSuite) {
370+
useJUnitJupiter()
371+
dependencies {
372+
implementation project()
373+
implementation testFixtures(project())
374+
}
375+
check.dependsOn intTest
376+
targets {
377+
all {
378+
testTask.configure {
379+
shouldRunAfter(test)
380+
mustRunAfter(test)
381+
}
382+
}
383+
}
384+
}
385+
}
386+
}
387+
388+
configurations {
389+
docs {
390+
description = 'Dependencies for generating configuration documentation'
391+
canBeResolved = true
392+
canBeConsumed = false
393+
visible = false
394+
}
395+
}
396+
363397
dependencies {
364398
api project(':iceberg-api')
365399
implementation project(':iceberg-common')
@@ -384,11 +418,16 @@ project(':iceberg-core') {
384418
exclude group: 'org.slf4j', module: 'slf4j-log4j12'
385419
}
386420

421+
// OAuth2
422+
implementation libs.nimbus.oauth2.oidc.sdk
423+
implementation libs.nimbus.jose.jwt
424+
387425
testImplementation libs.jetty.servlet
388426
testImplementation libs.jakarta.servlet
389427
testImplementation libs.jetty.server
390428
testImplementation libs.mockserver.netty
391429
testImplementation libs.mockserver.client.java
430+
testImplementation libs.bouncycastle.bcpkix
392431
testImplementation libs.sqlite.jdbc
393432
testImplementation libs.derby.core
394433
testImplementation libs.derby.tools
@@ -398,7 +437,108 @@ project(':iceberg-core') {
398437
exclude group: 'junit'
399438
}
400439
testImplementation libs.awaitility
440+
441+
testFixturesApi libs.junit.jupiter
442+
testFixturesApi libs.junit.pioneer
443+
testFixturesApi libs.assertj.core
444+
testFixturesApi libs.mockito.core
445+
446+
testFixturesApi libs.httpcomponents.httpclient5
447+
testFixturesApi libs.nimbus.oauth2.oidc.sdk
448+
testFixturesApi libs.nimbus.jose.jwt
449+
450+
testFixturesApi libs.mockserver.netty
451+
testFixturesApi libs.mockserver.client.java
452+
453+
testFixturesApi libs.testcontainers
454+
testFixturesApi libs.testcontainers.junit.jupiter
455+
testFixturesApi libs.testcontainers.keycloak
456+
457+
testFixturesApi libs.keycloak.admin.client
458+
459+
testFixturesImplementation project(path: ':iceberg-bundled-guava', configuration: 'shadow')
460+
461+
testFixturesAnnotationProcessor libs.immutables.value
462+
testFixturesCompileOnly libs.immutables.value
463+
464+
intTestImplementation project(path: ':iceberg-api', configuration: 'testArtifacts') // for CatalogTests
465+
intTestImplementation project(path: ':iceberg-core', configuration: 'testArtifacts') // for CatalogTests
466+
intTestImplementation project(path: ':iceberg-bundled-guava', configuration: 'shadow')
467+
468+
intTestImplementation libs.jetty.servlet
469+
intTestImplementation libs.jakarta.servlet
470+
intTestImplementation libs.jetty.server
471+
472+
docs 'com.thoughtworks.qdox:qdox:2.2.0'
473+
docs project(path: ':iceberg-bundled-guava', configuration: 'shadow')
401474
}
475+
476+
sourceSets {
477+
docs {
478+
java {
479+
srcDir 'src/docs/java'
480+
}
481+
resources {
482+
srcDir 'src/docs/resources'
483+
}
484+
compileClasspath += configurations.docs
485+
runtimeClasspath += configurations.docs
486+
}
487+
}
488+
489+
tasks.named('processDocsResources', ProcessResources).configure {
490+
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
491+
}
492+
493+
tasks.register('generateOAuth2Docs', JavaExec) {
494+
group = 'documentation'
495+
description = 'Generates REST OAuth2 configuration documentation from OAuth2Config'
496+
mainClass.set('org.apache.iceberg.rest.auth.oauth2.docs.OAuth2DocumentationGenerator')
497+
classpath = sourceSets.docs.runtimeClasspath
498+
499+
def inputFile = project.file('src/main/java/org/apache/iceberg/rest/auth/oauth2/OAuth2Config.java')
500+
def outputFile = rootProject.file('docs/docs/oauth2-configuration.md')
501+
502+
inputs.files(inputFile)
503+
outputs.file(outputFile)
504+
505+
args(inputFile.absolutePath, outputFile.absolutePath)
506+
507+
doFirst { outputFile.parentFile.mkdirs() }
508+
}
509+
510+
assemble.dependsOn('generateOAuth2Docs')
511+
512+
tasks.register('checkOAuth2Docs', JavaExec) {
513+
group = 'verification'
514+
description = 'Checks that the OAuth2 configuration documentation is up to date'
515+
mainClass.set('org.apache.iceberg.rest.auth.oauth2.docs.OAuth2DocumentationGenerator')
516+
classpath = sourceSets.docs.runtimeClasspath
517+
mustRunAfter('generateOAuth2Docs')
518+
519+
def inputFile = project.file('src/main/java/org/apache/iceberg/rest/auth/oauth2/OAuth2Config.java')
520+
def committedFile = rootProject.file('docs/docs/oauth2-configuration.md')
521+
def tempFile = project.layout.buildDirectory.file('generated-docs/oauth2-configuration.md')
522+
523+
inputs.files(inputFile, committedFile)
524+
outputs.file(tempFile)
525+
526+
args(inputFile.absolutePath, tempFile.get().asFile.absolutePath)
527+
528+
doFirst { tempFile.get().asFile.parentFile.mkdirs() }
529+
530+
doLast {
531+
def expected = tempFile.get().asFile.text
532+
def actual = committedFile.text
533+
if (expected != actual) {
534+
throw new GradleException(
535+
"OAuth2 configuration documentation is out of date. " +
536+
"Please run './gradlew :iceberg-core:generateOAuth2Docs' to update it.")
537+
}
538+
}
539+
}
540+
541+
check.dependsOn('checkOAuth2Docs')
402542
}
403543

404544
project(':iceberg-data') {

0 commit comments

Comments
 (0)