diff --git a/java-spiffe-core/src/main/java/io/spiffe/svid/jwtsvid/JwtSvidSource.java b/java-spiffe-core/src/main/java/io/spiffe/svid/jwtsvid/JwtSvidSource.java index b261110..c46d583 100644 --- a/java-spiffe-core/src/main/java/io/spiffe/svid/jwtsvid/JwtSvidSource.java +++ b/java-spiffe-core/src/main/java/io/spiffe/svid/jwtsvid/JwtSvidSource.java @@ -8,6 +8,16 @@ import io.spiffe.spiffeid.SpiffeId; */ public interface JwtSvidSource { + /** + * Fetches a JWT-SVID from the source with the given audiences. + * + * @param audience the audience + * @param extraAudiences a list of extra audiences as an array of String + * @return a {@link JwtSvid} + * @throws JwtSvidException when there is an error fetching the JWT SVID + */ + JwtSvid fetchJwtSvid(String audience, String... extraAudiences) throws JwtSvidException; + /** * Fetches a JWT-SVID from the source with the given subject and audiences. * diff --git a/java-spiffe-core/src/main/java/io/spiffe/workloadapi/DefaultWorkloadApiClient.java b/java-spiffe-core/src/main/java/io/spiffe/workloadapi/DefaultWorkloadApiClient.java index a0e32d9..3863772 100644 --- a/java-spiffe-core/src/main/java/io/spiffe/workloadapi/DefaultWorkloadApiClient.java +++ b/java-spiffe-core/src/main/java/io/spiffe/workloadapi/DefaultWorkloadApiClient.java @@ -181,6 +181,19 @@ public final class DefaultWorkloadApiClient implements WorkloadApiClient { this.cancellableContexts.add(cancellableContext); } + /** + * {@inheritDoc} + */ + @Override + public JwtSvid fetchJwtSvid(@NonNull String audience, String... extraAudience) throws JwtSvidException { + final Set audParam = createAudienceSet(audience, extraAudience); + try (val cancellableContext = Context.current().withCancellation()) { + return cancellableContext.call(() -> callFetchJwtSvid(audParam)); + } catch (Exception e) { + throw new JwtSvidException("Error fetching JWT SVID", e); + } + } + /** * {@inheritDoc} */ @@ -289,6 +302,14 @@ public final class DefaultWorkloadApiClient implements WorkloadApiClient { return JwtSvid.parseInsecure(response.getSvids(0).getSvid(), audience); } + private JwtSvid callFetchJwtSvid(final Set audience) throws JwtSvidException { + val jwtSvidRequest = Workload.JWTSVIDRequest.newBuilder() + .addAllAudience(audience) + .build(); + val response = workloadApiBlockingStub.fetchJWTSVID(jwtSvidRequest); + return JwtSvid.parseInsecure(response.getSvids(0).getSvid(), audience); + } + private JwtBundleSet callFetchBundles() throws JwtBundleException { val request = Workload.JWTBundlesRequest.newBuilder().build(); val bundlesResponse = workloadApiBlockingStub.fetchJWTBundles(request); diff --git a/java-spiffe-core/src/main/java/io/spiffe/workloadapi/JwtSource.java b/java-spiffe-core/src/main/java/io/spiffe/workloadapi/JwtSource.java index 29012bf..ed79c0d 100644 --- a/java-spiffe-core/src/main/java/io/spiffe/workloadapi/JwtSource.java +++ b/java-spiffe-core/src/main/java/io/spiffe/workloadapi/JwtSource.java @@ -107,6 +107,14 @@ public class JwtSource implements JwtSvidSource, BundleSource, Closea return jwtSource; } + @Override + public JwtSvid fetchJwtSvid(String audience, String... extraAudiences) throws JwtSvidException { + if (isClosed()) { + throw new IllegalStateException("JWT SVID source is closed"); + } + return workloadApiClient.fetchJwtSvid(audience, extraAudiences); + } + /** * Fetches a new JWT SVID from the Workload API for the given subject SPIFFE ID and audiences. * diff --git a/java-spiffe-core/src/main/java/io/spiffe/workloadapi/WorkloadApiClient.java b/java-spiffe-core/src/main/java/io/spiffe/workloadapi/WorkloadApiClient.java index 658f8df..29663ef 100644 --- a/java-spiffe-core/src/main/java/io/spiffe/workloadapi/WorkloadApiClient.java +++ b/java-spiffe-core/src/main/java/io/spiffe/workloadapi/WorkloadApiClient.java @@ -36,6 +36,16 @@ public interface WorkloadApiClient extends Closeable { */ void watchX509Context(@NonNull Watcher watcher); + /** + * Fetches a SPIFFE JWT-SVID on one-shot blocking call. + * + * @param audience the audience of the JWT-SVID + * @param extraAudience the extra audience for the JWT_SVID + * @return an instance of a {@link JwtSvid} + * @throws JwtSvidException if there is an error fetching or processing the JWT from the Workload API + */ + JwtSvid fetchJwtSvid(@NonNull String audience, String... extraAudience) throws JwtSvidException; + /** * Fetches a SPIFFE JWT-SVID on one-shot blocking call. * diff --git a/java-spiffe-core/src/test/java/io/spiffe/workloadapi/JwtSourceTest.java b/java-spiffe-core/src/test/java/io/spiffe/workloadapi/JwtSourceTest.java index 6d36bda..a1834e4 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/workloadapi/JwtSourceTest.java +++ b/java-spiffe-core/src/test/java/io/spiffe/workloadapi/JwtSourceTest.java @@ -61,7 +61,7 @@ class JwtSourceTest { } @Test - void testFetchJwtSvid() { + void testFetchJwtSvidWithSubject() { try { JwtSvid svid = jwtSource.fetchJwtSvid(SpiffeId.parse("spiffe://example.org/workload-server"), "aud1", "aud2", "aud3"); assertNotNull(svid); @@ -72,6 +72,18 @@ class JwtSourceTest { } } + @Test + void testFetchJwtSvidWithoutSubject() { + try { + JwtSvid svid = jwtSource.fetchJwtSvid("aud1", "aud2", "aud3"); + assertNotNull(svid); + assertEquals(SpiffeId.parse("spiffe://example.org/workload-server"), svid.getSpiffeId()); + assertEquals(Sets.newHashSet("aud1", "aud2", "aud3"), svid.getAudience()); + } catch (JwtSvidException e) { + fail(e); + } + } + @Test void testFetchJwtSvid_SourceIsClosed_ThrowsIllegalStateException() { jwtSource.close(); diff --git a/java-spiffe-core/src/test/java/io/spiffe/workloadapi/WorkloadApiClientStub.java b/java-spiffe-core/src/test/java/io/spiffe/workloadapi/WorkloadApiClientStub.java index 1ab50da..83555a0 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/workloadapi/WorkloadApiClientStub.java +++ b/java-spiffe-core/src/test/java/io/spiffe/workloadapi/WorkloadApiClientStub.java @@ -39,6 +39,7 @@ public class WorkloadApiClientStub implements WorkloadApiClient { final String svid = "testdata/workloadapi/svid.der"; final String x509Bundle = "testdata/workloadapi/bundle.der"; final String jwtBundle = "testdata/workloadapi/bundle.json"; + final SpiffeId subject = SpiffeId.parse("spiffe://example.org/workload-server"); boolean closed; @@ -53,6 +54,11 @@ public class WorkloadApiClientStub implements WorkloadApiClient { watcher.onUpdate(update); } + @Override + public JwtSvid fetchJwtSvid(@NonNull final String audience, final String... extraAudience) throws JwtSvidException { + return generateJwtSvid(subject, audience, extraAudience); + } + @Override public JwtSvid fetchJwtSvid(@NonNull final SpiffeId subject, @NonNull final String audience, final String... extraAudience) throws JwtSvidException { return generateJwtSvid(subject, audience, extraAudience); diff --git a/java-spiffe-helper/src/test/java/io/spiffe/helper/keystore/WorkloadApiClientStub.java b/java-spiffe-helper/src/test/java/io/spiffe/helper/keystore/WorkloadApiClientStub.java index 40c4585..54515b5 100644 --- a/java-spiffe-helper/src/test/java/io/spiffe/helper/keystore/WorkloadApiClientStub.java +++ b/java-spiffe-helper/src/test/java/io/spiffe/helper/keystore/WorkloadApiClientStub.java @@ -42,6 +42,11 @@ public class WorkloadApiClientStub implements WorkloadApiClient { watcher.onUpdate(update); } + @Override + public JwtSvid fetchJwtSvid(@NonNull String audience, String... extraAudience) throws JwtSvidException { + return null; + } + @Override public JwtSvid fetchJwtSvid(@NonNull final SpiffeId subject, @NonNull final String audience, final String... extraAudience) throws JwtSvidException { return null;