11package io .opentdf .platform .sdk ;
22
3+ import com .connectrpc .ConnectException ;
4+ import com .google .gson .Gson ;
5+ import com .google .gson .JsonSyntaxException ;
6+ import com .google .gson .annotations .SerializedName ;
7+ import io .opentdf .platform .policy .Algorithm ;
8+ import io .opentdf .platform .policy .SimpleKasKey ;
9+ import io .opentdf .platform .policy .SimpleKasPublicKey ;
310import io .opentdf .platform .policy .Value ;
11+ import io .opentdf .platform .wellknownconfiguration .GetWellKnownConfigurationRequest ;
12+ import io .opentdf .platform .wellknownconfiguration .GetWellKnownConfigurationResponse ;
413import org .slf4j .Logger ;
514import org .slf4j .LoggerFactory ;
615
716import java .util .ArrayList ;
17+ import java .util .Collections ;
818import java .util .HashMap ;
919import java .util .List ;
1020import java .util .Map ;
1121import java .util .Objects ;
1222import java .util .Optional ;
1323import java .util .UUID ;
1424
15- import static io .opentdf .platform .sdk .Autoconfigure .Granter .generatePlanFromDefaultKases ;
16-
1725public class Planner {
26+ private static final String BASE_KEY = "base_key" ;
1827 private final Config .TDFConfig tdfConfig ;
19- private final SDK .Services sdkServices ;
28+ private final SDK .Services services ;
29+
2030
2131 private static final Logger logger = LoggerFactory .getLogger (Planner .class );
2232
2333 public Planner (Config .TDFConfig config , SDK .Services services ) {
24- tdfConfig = Objects .requireNonNull (config );
25- sdkServices = Objects .requireNonNull (services ) ;
34+ this . tdfConfig = Objects .requireNonNull (config );
35+ this . services = Objects .requireNonNull (services );
2636 }
2737
2838 private static String getUUID () {
@@ -31,43 +41,107 @@ private static String getUUID() {
3141
3242 Map <String , List <Config .KASInfo >> getSplits (Config .TDFConfig tdfConfig ) {
3343 List <Autoconfigure .KeySplitStep > splitPlan ;
34- List <String > defaultKases = defaultKases (tdfConfig );
3544 if (tdfConfig .autoconfigure ) {
45+ if (tdfConfig .splitPlan != null && !tdfConfig .splitPlan .isEmpty ()) {
46+ throw new IllegalArgumentException ("cannot use autoconfigure with a split plan provided in the TDFConfig" );
47+ }
3648 splitPlan = getAutoconfigurePlan (tdfConfig );
3749 } else if (tdfConfig .splitPlan == null || tdfConfig .splitPlan .isEmpty ()) {
38- splitPlan = defaultKases .isEmpty ()
39- ? createPlanFromBaseKey ()
40- : generatePlanFromDefaultKases (defaultKases , Planner ::getUUID );
50+ splitPlan = generatePlanFromProvidedKases (tdfConfig .kasInfoList );
4151 } else {
4252 splitPlan = tdfConfig .splitPlan ;
4353 }
4454
4555 if (tdfConfig .kasInfoList .isEmpty () && tdfConfig .splitPlan .isEmpty ()) {
4656 throw new SDK .KasInfoMissing ("kas information is missing, no key access template specified or inferred" );
4757 }
48-
49- // split plan: restructure by conjunctions
5058 return fillInKeys (tdfConfig , splitPlan );
5159 }
5260
5361 private List <Autoconfigure .KeySplitStep > getAutoconfigurePlan (Config .TDFConfig tdfConfig ) {
54- if (tdfConfig .splitPlan != null && !tdfConfig .splitPlan .isEmpty ()) {
55- throw new IllegalArgumentException ("cannot use autoconfigure with a split plan provided in the TDFConfig" );
56- }
5762 Autoconfigure .Granter granter = new Autoconfigure .Granter (new ArrayList <>());
5863 if (tdfConfig .attributeValues != null && !tdfConfig .attributeValues .isEmpty ()) {
59- granter = Autoconfigure .newGranterFromAttributes (sdkServices .kas ().getKeyCache (), tdfConfig .attributeValues .toArray (new Value [0 ]));
64+ granter = Autoconfigure .newGranterFromAttributes (services .kas ().getKeyCache (), tdfConfig .attributeValues .toArray (new Value [0 ]));
6065 } else if (tdfConfig .attributes != null && !tdfConfig .attributes .isEmpty ()) {
61- granter = Autoconfigure .newGranterFromService (sdkServices .attributes (), sdkServices .kas ().getKeyCache (),
66+ granter = Autoconfigure .newGranterFromService (services .attributes (), services .kas ().getKeyCache (),
6267 tdfConfig .attributes .toArray (new Autoconfigure .AttributeValueFQN [0 ]));
6368 }
64- return granter .getSplits (defaultKases (tdfConfig ), Planner ::getUUID , Optional ::empty );
69+ return granter .getSplits (defaultKases (tdfConfig ), Planner ::getUUID , this ::fetchBaseKey );
70+ }
71+
72+ List <Autoconfigure .KeySplitStep > generatePlanFromProvidedKases (List <Config .KASInfo > kases ) {
73+ if (kases .size () == 1 ) {
74+ var kasInfo = kases .get (0 );
75+ return Collections .singletonList (new Autoconfigure .KeySplitStep (kasInfo .URL , "" , kasInfo .KID ));
76+ }
77+ List <Autoconfigure .KeySplitStep > splitPlan = new ArrayList <>();
78+ for (var kasInfo : kases ) {
79+ splitPlan .add (new Autoconfigure .KeySplitStep (kasInfo .URL , getUUID (), kasInfo .KID ));
80+ }
81+ return splitPlan ;
82+ }
83+
84+ Optional <SimpleKasKey > fetchBaseKey () {
85+ var responseMessage = services .wellknown ()
86+ .getWellKnownConfigurationBlocking (GetWellKnownConfigurationRequest .getDefaultInstance (), Collections .emptyMap ())
87+ .execute ();
88+ GetWellKnownConfigurationResponse response ;
89+ try {
90+ response = RequestHelper .getOrThrow (responseMessage );
91+ } catch (ConnectException e ) {
92+ logger .error ("unable to retrieve configuration from well known endpoint" , e );
93+ throw new SDKException ("unable to retrieve base key from well known endpoint" , e );
94+ }
95+
96+ String baseKeyJson ;
97+ try {
98+ baseKeyJson = response
99+ .getConfiguration ()
100+ .getFieldsOrThrow (BASE_KEY )
101+ .getStringValue ();
102+ } catch (IllegalArgumentException e ) {
103+ logger .info ( "no `" + BASE_KEY + "` found in well known configuration." , e );
104+ return Optional .empty ();
105+ }
106+
107+ BaseKey baseKey ;
108+ try {
109+ baseKey = gson .fromJson (baseKeyJson , BaseKey .class );
110+ } catch (JsonSyntaxException e ) {
111+ throw new SDKException ("base key in well known configuration is malformed [" + baseKeyJson + "]" , e );
112+ }
113+
114+ if (baseKey == null || baseKey .kasUrl == null || baseKey .publicKey == null || baseKey .publicKey .kid == null || baseKey .publicKey .pem == null || baseKey .publicKey .algorithm == null ) {
115+ throw new SDKException ("base key in well known configuration is missing required fields [" + baseKeyJson + "]" );
116+ }
117+
118+ return Optional .of (SimpleKasKey .newBuilder ()
119+ .setKasUri (baseKey .kasUrl )
120+ .setPublicKey (SimpleKasPublicKey .newBuilder ()
121+ .setKid (baseKey .publicKey .kid )
122+ .setAlgorithm (baseKey .publicKey .algorithm )
123+ .setPem (baseKey .publicKey .pem )
124+ .build ())
125+ .build ());
65126 }
66127
67- private List <Autoconfigure .KeySplitStep > createPlanFromBaseKey () {
68- return null ;
128+ private static Gson gson = new Gson ();
129+
130+ private static class BaseKey {
131+ @ SerializedName ("kas_url" )
132+ String kasUrl ;
133+
134+ @ SerializedName ("public_key" )
135+ Key publicKey ;
136+
137+ private static class Key {
138+ String kid ;
139+ String pem ;
140+ Algorithm algorithm ;
141+ }
69142 }
70143
144+
71145 private Map <String , List <Config .KASInfo >> fillInKeys (Config .TDFConfig tdfConfig , List <Autoconfigure .KeySplitStep > splitPlan ) {
72146 Map <String , List <Config .KASInfo >> conjunction = new HashMap <>();
73147 var latestKASInfo = new HashMap <String , Config .KASInfo >();
@@ -87,7 +161,7 @@ private Map<String, List<Config.KASInfo>> fillInKeys(Config.TDFConfig tdfConfig,
87161 var getKI = new Config .KASInfo ();
88162 getKI .URL = splitInfo .kas ;
89163 getKI .Algorithm = tdfConfig .wrappingKeyType .toString ();
90- getKI = sdkServices .kas ().getPublicKey (getKI );
164+ getKI = services .kas ().getPublicKey (getKI );
91165 latestKASInfo .put (splitInfo .kas , getKI );
92166 ki = getKI ;
93167 }
0 commit comments