1111import com .nimbusds .oauth2 .sdk .id .ClientID ;
1212import com .nimbusds .oauth2 .sdk .id .Issuer ;
1313import com .nimbusds .openid .connect .sdk .op .OIDCProviderMetadata ;
14+ import io .grpc .Channel ;
1415import io .grpc .ManagedChannel ;
1516import io .grpc .ManagedChannelBuilder ;
1617import io .grpc .Status ;
18+ import io .grpc .StatusRuntimeException ;
1719import io .opentdf .platform .wellknownconfiguration .GetWellKnownConfigurationRequest ;
1820import io .opentdf .platform .wellknownconfiguration .GetWellKnownConfigurationResponse ;
1921import io .opentdf .platform .wellknownconfiguration .WellKnownServiceGrpc ;
22+ import org .slf4j .Logger ;
23+ import org .slf4j .LoggerFactory ;
2024
2125import java .io .IOException ;
2226import java .util .UUID ;
27+ import java .util .function .Function ;
2328
2429/**
2530 * A builder class for creating instances of the SDK class.
@@ -30,9 +35,13 @@ public class SDKBuilder {
3035 private ClientAuthentication clientAuth = null ;
3136 private Boolean usePlainText ;
3237
38+ private static final Logger logger = LoggerFactory .getLogger (SDKBuilder .class );
39+
3340 public static SDKBuilder newBuilder () {
3441 SDKBuilder builder = new SDKBuilder ();
3542 builder .usePlainText = false ;
43+ builder .clientAuth = null ;
44+ builder .platformEndpoint = null ;
3645
3746 return builder ;
3847 }
@@ -54,8 +63,16 @@ public SDKBuilder useInsecurePlaintextConnection(Boolean usePlainText) {
5463 return this ;
5564 }
5665
57- // this is not exposed publicly so that it can be tested
58- ManagedChannel buildChannel () {
66+ private GRPCAuthInterceptor getGrpcAuthInterceptor () {
67+ if (platformEndpoint == null ) {
68+ throw new SDKException ("cannot build an SDK without specifying the platform endpoint" );
69+ }
70+
71+ if (clientAuth == null ) {
72+ // this simplifies things for now, if we need to support this case we can revisit
73+ throw new SDKException ("cannot build an SDK without specifying OAuth credentials" );
74+ }
75+
5976 // we don't add the auth listener to this channel since it is only used to call the
6077 // well known endpoint
6178 ManagedChannel bootstrapChannel = null ;
@@ -65,7 +82,7 @@ ManagedChannel buildChannel() {
6582 var stub = WellKnownServiceGrpc .newBlockingStub (bootstrapChannel );
6683 try {
6784 config = stub .getWellKnownConfiguration (GetWellKnownConfigurationRequest .getDefaultInstance ());
68- } catch (Exception e ) {
85+ } catch (StatusRuntimeException e ) {
6986 Status status = Status .fromThrowable (e );
7087 throw new SDKException (String .format ("Got grpc status [%s] when getting configuration" , status ), e );
7188 }
@@ -82,7 +99,7 @@ ManagedChannel buildChannel() {
8299 .getFieldsOrThrow (PLATFORM_ISSUER )
83100 .getStringValue ();
84101
85- } catch (Exception e ) {
102+ } catch (StatusRuntimeException e ) {
86103 throw new SDKException ("Error getting the issuer from the platform" , e );
87104 }
88105
@@ -104,24 +121,39 @@ ManagedChannel buildChannel() {
104121 throw new SDKException ("Error generating DPoP key" , e );
105122 }
106123
107- GRPCAuthInterceptor interceptor = new GRPCAuthInterceptor (clientAuth , rsaKey , providerMetadata .getTokenEndpointURI ());
124+ return new GRPCAuthInterceptor (clientAuth , rsaKey , providerMetadata .getTokenEndpointURI ());
125+ }
108126
109- return getManagedChannelBuilder ()
110- .intercept (interceptor )
111- .build ();
127+ SDK .Services buildServices () {
128+ var authInterceptor = getGrpcAuthInterceptor ();
129+ var channel = getManagedChannelBuilder ().intercept (authInterceptor ).build ();
130+ var client = new KASClient (getChannelFactory (authInterceptor ));
131+ return SDK .Services .newServices (channel , client );
112132 }
113133
114134 public SDK build () {
115- return new SDK (SDK . Services . newServices ( buildChannel () ));
135+ return new SDK (buildServices ( ));
116136 }
117137
118138 private ManagedChannelBuilder <?> getManagedChannelBuilder () {
119- ManagedChannelBuilder <?> channelBuilder = ManagedChannelBuilder
120- .forTarget (platformEndpoint );
139+ ManagedChannelBuilder <?> channelBuilder = ManagedChannelBuilder .forTarget (platformEndpoint );
121140
122141 if (usePlainText ) {
123142 channelBuilder = channelBuilder .usePlaintext ();
124143 }
125144 return channelBuilder ;
126145 }
146+
147+ Function <SDK .KASInfo , Channel > getChannelFactory (GRPCAuthInterceptor authInterceptor ) {
148+ var pt = usePlainText ; // no need to have the builder be able to influence things from beyond the grave
149+ return (SDK .KASInfo kasInfo ) -> {
150+ ManagedChannelBuilder <?> channelBuilder = ManagedChannelBuilder
151+ .forTarget (kasInfo .getAddress ())
152+ .intercept (authInterceptor );
153+ if (pt ) {
154+ channelBuilder = channelBuilder .usePlaintext ();
155+ }
156+ return channelBuilder .build ();
157+ };
158+ }
127159}
0 commit comments