Skip to content
Merged
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
22 changes: 22 additions & 0 deletions src/main/java/land/oras/Registry.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import java.util.function.Supplier;
import land.oras.auth.AuthProvider;
import land.oras.auth.AuthStoreAuthenticationProvider;
import land.oras.auth.BearerTokenProvider;
import land.oras.auth.HttpClient;
import land.oras.auth.NoAuthProvider;
import land.oras.auth.RegistriesConf;
Expand Down Expand Up @@ -294,6 +295,15 @@ public Registry copy(String newRegistry) {
return new Builder().from(this).withRegistry(newRegistry).build();
}

/**
* Return a new registry with a new auth external token and same settings
* @param authToken The new refreshed token
* @return The new registry
*/
public Registry withAuthToken(String authToken) {
return new Builder().from(this).withAuthToken(authToken).build();
}

/**
* Return a new registry as insecure but with same settings
* @return The new registry
Expand Down Expand Up @@ -1281,6 +1291,18 @@ public Builder withAuthProvider(AuthProvider authProvider) {
return this;
}

/**
* Use given auth token for the registry.
* Useful when the auth token is obtained by other mean (like a token exchange).
* Caller are responsible to handle token expiration if any
* @param authToken The auth token
* @return The builder
*/
public Builder withAuthToken(String authToken) {
registry.setAuthProvider(new BearerTokenProvider(authToken));
return this;
}

/**
* Set the maximum number of concurrent downloads when pulling an artifact with multiple layers. Default is 4.
* @param parallelism The maximum number of parallel uploads/download
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/land/oras/auth/BearerTokenProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ public final class BearerTokenProvider implements AuthProvider {
*/
public BearerTokenProvider() {}

/**
* Create a new bearer token provider
* @param token The token
*/
public BearerTokenProvider(String token) {
setToken(new HttpClient.TokenResponse(token, null, null, null, null));
}

/**
* Get the token
* @return The token
Expand Down
28 changes: 28 additions & 0 deletions src/test/java/land/oras/RegistryWireMockTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,34 @@ class RegistryWireMockTest {
@TempDir
private static Path homeDir3;

@Test
void shouldPassBearerTokenWithExternalRequestedToken(WireMockRuntimeInfo wmRuntimeInfo) {
Registry registry = Registry.Builder.builder()
.insecure()
.withAuthToken("insecure-token")
.build();

// Ensure WireMock accept only our token
WireMock wireMock = wmRuntimeInfo.getWireMock();
wireMock.register(WireMock.get(WireMock.urlEqualTo("/v2/library/artifact-text/tags/list"))
.withHeader("Authorization", equalTo("Bearer insecure-token"))
.willReturn(WireMock.okJson(JsonUtils.toJson(new Tags("artifact-text", List.of("latest", "0.1.1"))))));
wireMock.register(WireMock.get(WireMock.urlEqualTo("/v2/library/artifact-text/tags/list"))
.withHeader("Authorization", equalTo("Bearer invalid-token"))
.willReturn(WireMock.unauthorized()));

registry.getTags(ContainerRef.parse("%s/library/artifact-text"
.formatted(wmRuntimeInfo.getHttpBaseUrl().replace("http://", ""))));

// Ensure it fail with invalid token
final Registry newRegistry = registry.withAuthToken("invalid-token");
OrasException e = assertThrows(
OrasException.class,
() -> newRegistry.getTags(ContainerRef.parse("%s/library/artifact-text"
.formatted(wmRuntimeInfo.getHttpBaseUrl().replace("http://", "")))));
assertEquals(401, e.getStatusCode());
}

@Test
void shouldFailToGetManifestOn403(WireMockRuntimeInfo wmRuntimeInfo) {

Expand Down
Loading