View Javadoc
1   /*
2    * Prometheus: Application Framework
3    * Copyright 2012-2026. Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6    * use this file except in compliance with the License.  You may obtain a copy
7    * of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
16   */
17  package io.github.tonywasher.joceanus.prometheus.data;
18  
19  import io.github.tonywasher.joceanus.metis.data.MetisDataDifference;
20  import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataFieldId;
21  import io.github.tonywasher.joceanus.metis.field.MetisFieldSet;
22  import io.github.tonywasher.joceanus.metis.field.MetisFieldVersionedSet;
23  import io.github.tonywasher.joceanus.metis.list.MetisListKey;
24  import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
25  import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
26  import io.github.tonywasher.joceanus.prometheus.data.PrometheusCryptographyData.PrometheusControlDataCtl;
27  import io.github.tonywasher.joceanus.prometheus.data.PrometheusCryptographyData.PrometheusCryptographicDataSet;
28  import io.github.tonywasher.joceanus.prometheus.data.PrometheusData.PrometheusDataSetCtl;
29  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataKeySet.PrometheusDataKeySetList;
30  import io.github.tonywasher.joceanus.prometheus.data.PrometheusEncrypted.PrometheusEncryptedDataItemCtl;
31  import io.github.tonywasher.joceanus.tethys.api.thread.TethysUIThreadStatusReport;
32  
33  import java.util.Iterator;
34  
35  /**
36   * Encrypted Data Item and List.
37   *
38   * @author Tony Washer
39   */
40  public abstract class PrometheusEncryptedDataItem
41          extends PrometheusDataItem
42          implements PrometheusEncryptedDataItemCtl {
43      /**
44       * Null Encryptor.
45       */
46      private static final PrometheusEncryptor NULL_ENCRYPTOR = new PrometheusEncryptor(new OceanusDataFormatter(), null);
47  
48      /**
49       * Report fields.
50       */
51      private static final MetisFieldVersionedSet<PrometheusEncryptedDataItem> FIELD_DEFS = MetisFieldVersionedSet.newVersionedFieldSet(PrometheusEncryptedDataItem.class);
52  
53      /*
54       * FieldIds.
55       */
56      static {
57          FIELD_DEFS.declareLinkField(PrometheusCryptographyDataType.DATAKEYSET);
58      }
59  
60      /**
61       * Standard Constructor. This creates a null encryption generator. This will be overridden when
62       * a DataKeySet is assigned to the item.
63       *
64       * @param pList the list that this item is associated with
65       * @param pId   the Id of the new item (or 0 if not yet known)
66       */
67      protected PrometheusEncryptedDataItem(final PrometheusEncryptedList<?> pList,
68                                            final Integer pId) {
69          super(pList, pId);
70      }
71  
72      /**
73       * Copy Constructor. This picks up the generator from the source item.
74       *
75       * @param pList   the list that this item is associated with
76       * @param pSource the source item
77       */
78      protected PrometheusEncryptedDataItem(final PrometheusEncryptedList<?> pList,
79                                            final PrometheusEncryptedDataItem pSource) {
80          super(pList, pSource);
81      }
82  
83      /**
84       * Values Constructor. This creates a null encryption generator. This will be overridden when a
85       * ControlKey is assigned to the item.
86       *
87       * @param pList   the list that this item is associated with
88       * @param pValues the data values
89       * @throws OceanusException on error
90       */
91      protected PrometheusEncryptedDataItem(final PrometheusEncryptedList<?> pList,
92                                            final PrometheusDataValues pValues) throws OceanusException {
93          super(pList, pValues);
94  
95          /* Access dataKeySet id */
96          final Integer myId = pValues.getValue(PrometheusCryptographyDataType.DATAKEYSET, Integer.class);
97          if (myId != null) {
98              setDataKeySet(myId);
99          }
100     }
101 
102     @Override
103     protected PrometheusEncryptedValues newVersionValues() {
104         return new PrometheusEncryptedValues(this);
105     }
106 
107     @Override
108     public PrometheusEncryptedValues getValues() {
109         return (PrometheusEncryptedValues) super.getValues();
110     }
111 
112     @Override
113     public PrometheusEncryptedValues getOriginalValues() {
114         return (PrometheusEncryptedValues) super.getOriginalValues();
115     }
116 
117     /**
118      * Get the DataKeySet for this item.
119      *
120      * @return the DataKeySet
121      */
122     public final PrometheusDataKeySet getDataKeySet() {
123         return getValues().getValue(PrometheusCryptographyDataType.DATAKEYSET, PrometheusDataKeySet.class);
124     }
125 
126     /**
127      * Get the DataKeySetId for this item.
128      *
129      * @return the DataKeySetId
130      */
131     public final Integer getDataKeySetId() {
132         final PrometheusDataKeySet mySet = getDataKeySet();
133         return (mySet == null)
134                 ? null
135                 : mySet.getIndexedId();
136     }
137 
138     /**
139      * Set the DataKeySet for this item.
140      *
141      * @param pSet the dataKeySet
142      */
143     private void setValueDataKeySet(final PrometheusDataKeySet pSet) {
144         getValues().setUncheckedValue(PrometheusCryptographyDataType.DATAKEYSET, pSet);
145     }
146 
147     /**
148      * Set the KeySet id for this item.
149      *
150      * @param pId the keySet id
151      */
152     private void setValueDataKeySet(final Integer pId) {
153         getValues().setUncheckedValue(PrometheusCryptographyDataType.DATAKEYSET, pId);
154     }
155 
156     @Override
157     public PrometheusEncryptor getEncryptor() {
158         final PrometheusDataKeySet myKeySet = getDataKeySet();
159         return myKeySet == null ? NULL_ENCRYPTOR : myKeySet.getEncryptor();
160     }
161 
162     @Override
163     public PrometheusEncryptedList<?> getList() {
164         return (PrometheusEncryptedList<?>) super.getList();
165     }
166 
167     /**
168      * Set DataKeySet.
169      *
170      * @param pKeySet the DataKeySet
171      */
172     protected final void setDataKeySet(final PrometheusDataKeySet pKeySet) {
173         setValueDataKeySet(pKeySet);
174     }
175 
176     /**
177      * Set Next DataKeySet.
178      */
179     protected final void setNextDataKeySet() {
180         setDataKeySet(getList().getNextDataKeySet());
181     }
182 
183     /**
184      * Set DataKeySet id.
185      *
186      * @param pKeySetId the KeySet Id
187      * @throws OceanusException on error
188      */
189     protected final void setDataKeySet(final Integer pKeySetId) throws OceanusException {
190         /* Store the id */
191         setValueDataKeySet(pKeySetId);
192 
193         /* Resolve the ControlKey */
194         final PrometheusDataSetCtl myData = getDataSet();
195         resolveDataLink(PrometheusCryptographyDataType.DATAKEYSET, PrometheusCryptographyDataType.DATAKEYSET);
196     }
197 
198     /**
199      * Set encrypted value.
200      *
201      * @param pFieldId the fieldId to set
202      * @param pValue   the value to set
203      * @throws OceanusException on error
204      */
205     protected final void setEncryptedValue(final MetisDataFieldId pFieldId,
206                                            final Object pValue) throws OceanusException {
207         /* Obtain the existing value */
208         final MetisFieldDef myFieldDef = getDataFieldSet().getField(pFieldId);
209         final PrometheusEncryptedValues myValueSet = getValues();
210         Object myCurrent = myValueSet.getValue(myFieldDef);
211 
212         /* Handle switched usage */
213         if (myCurrent != null && !(myCurrent instanceof PrometheusEncryptedPair)) {
214             myCurrent = null;
215         }
216 
217         /* Create the new encrypted value */
218         final PrometheusEncryptedPair myCurr = (PrometheusEncryptedPair) myCurrent;
219         final PrometheusEncryptedPair myField = getEncryptor().encryptValue(myCurr, pValue);
220 
221         /* Store the new value */
222         myValueSet.setUncheckedValue(myFieldDef, myField);
223     }
224 
225     /**
226      * Set encrypted value.
227      *
228      * @param pFieldId   the fieldId to set
229      * @param pEncrypted the encrypted value to set
230      * @throws OceanusException on error
231      */
232     protected final void setEncryptedValue(final MetisDataFieldId pFieldId,
233                                            final byte[] pEncrypted) throws OceanusException {
234         /* Create the new encrypted value */
235         final MetisFieldDef myFieldDef = getDataFieldSet().getField(pFieldId);
236         final PrometheusEncryptedPair myField = getEncryptor().decryptValue(pEncrypted, myFieldDef);
237 
238         /* Store the new value */
239         getValues().setValue(myFieldDef, myField);
240     }
241 
242     /**
243      * Set encrypted value.
244      *
245      * @param pFieldId   the fieldId to set
246      * @param pEncrypted the encrypted value to set
247      * @param pClazz     the class to decrypt to
248      * @throws OceanusException on error
249      */
250     protected final void setEncryptedValue(final MetisDataFieldId pFieldId,
251                                            final byte[] pEncrypted,
252                                            final Class<?> pClazz) throws OceanusException {
253         /* Create the new encrypted value */
254         final MetisFieldDef myFieldDef = getDataFieldSet().getField(pFieldId);
255         final PrometheusEncryptedPair myField = getEncryptor().decryptValue(pEncrypted, pClazz);
256 
257         /* Store the new value */
258         getValues().setUncheckedValue(myFieldDef, myField);
259     }
260 
261     /**
262      * Determine whether two ValuePair objects differ.
263      *
264      * @param pCurr The current Pair
265      * @param pNew  The new Pair
266      * @return <code>true</code> if the objects differ, <code>false</code> otherwise
267      */
268     public static MetisDataDifference getDifference(final PrometheusEncryptedPair pCurr,
269                                                     final PrometheusEncryptedPair pNew) {
270         /* Handle case where current value is null */
271         if (pCurr == null) {
272             return (pNew != null)
273                     ? MetisDataDifference.DIFFERENT
274                     : MetisDataDifference.IDENTICAL;
275         }
276 
277         /* Handle case where new value is null */
278         if (pNew == null) {
279             return MetisDataDifference.DIFFERENT;
280         }
281 
282         /* Handle Standard cases */
283         return pCurr.differs(pNew);
284     }
285 
286     @Override
287     public void resolveDataSetLinks() throws OceanusException {
288         /* Resolve the ControlKey */
289         final PrometheusDataSetCtl myData = getDataSet();
290         resolveDataLink(PrometheusCryptographyDataType.DATAKEYSET, PrometheusCryptographyDataType.DATAKEYSET);
291     }
292 
293     /**
294      * Adopt security for all encrypted values.
295      *
296      * @param pKeySet the new KeySet
297      * @param pBase   the base item
298      * @throws OceanusException on error
299      */
300     protected void adoptSecurity(final PrometheusDataKeySet pKeySet,
301                                  final PrometheusEncryptedDataItem pBase) throws OceanusException {
302         /* Set the DataKeySet */
303         setValueDataKeySet(pKeySet);
304 
305         /* Access underlying values if they exist */
306         final PrometheusEncryptedValues myBaseValues = pBase.getValues();
307 
308         /* Try to adopt the underlying */
309         getValues().adoptSecurity(myBaseValues);
310     }
311 
312     /**
313      * Initialise security for all encrypted values.
314      *
315      * @param pKeySet the new KeySet
316      * @throws OceanusException on error
317      */
318     protected void initialiseSecurity(final PrometheusDataKeySet pKeySet) throws OceanusException {
319         /* Set the DataKeySet */
320         setValueDataKeySet(pKeySet);
321 
322         /* Initialise security */
323         getValues().adoptSecurity(null);
324     }
325 
326     /**
327      * Update security for all encrypted values.
328      *
329      * @param pKeySet the new KeySet
330      * @throws OceanusException on error
331      */
332     protected void updateSecurity(final PrometheusDataKeySet pKeySet) throws OceanusException {
333         /* Ignore call if we have the same keySet */
334         if (pKeySet.equals(getDataKeySet())) {
335             return;
336         }
337 
338         /* Store the current detail into history */
339         pushHistory();
340 
341         /* Set the DataKeySet */
342         setDataKeySet(pKeySet);
343 
344         /* Update all elements */
345         getValues().updateSecurity();
346     }
347 
348     /**
349      * Encrypted DataList.
350      *
351      * @param <T> the item type
352      */
353     public abstract static class PrometheusEncryptedList<T extends PrometheusEncryptedDataItem>
354             extends PrometheusDataList<T> {
355         /*
356          * Report fields.
357          */
358         static {
359             MetisFieldSet.newFieldSet(PrometheusEncryptedList.class);
360         }
361 
362         /**
363          * Construct an empty CORE encrypted list.
364          *
365          * @param pBaseClass the class of the underlying object
366          * @param pData      the DataSet for the list
367          * @param pItemType  the item type
368          */
369         protected PrometheusEncryptedList(final Class<T> pBaseClass,
370                                           final PrometheusCryptographicDataSet pData,
371                                           final MetisListKey pItemType) {
372             this(pBaseClass, pData, pItemType, PrometheusListStyle.CORE);
373         }
374 
375         /**
376          * Construct a generic encrypted list.
377          *
378          * @param pBaseClass the class of the underlying object
379          * @param pData      the DataSet for the list
380          * @param pItemType  the list type
381          * @param pStyle     the style of the list
382          */
383         protected PrometheusEncryptedList(final Class<T> pBaseClass,
384                                           final PrometheusCryptographicDataSet pData,
385                                           final MetisListKey pItemType,
386                                           final PrometheusListStyle pStyle) {
387             super(pBaseClass, pData, pItemType, pStyle);
388         }
389 
390         /**
391          * Constructor for a cloned List.
392          *
393          * @param pSource the source List
394          */
395         protected PrometheusEncryptedList(final PrometheusEncryptedList<T> pSource) {
396             super(pSource);
397         }
398 
399         @Override
400         public PrometheusCryptographicDataSet getDataSet() {
401             return (PrometheusCryptographicDataSet) super.getDataSet();
402         }
403 
404         /**
405          * Get the active controlKey.
406          *
407          * @return the active controlKey
408          */
409         private PrometheusControlKey getControlKey() {
410             final PrometheusControlDataCtl myControl = getDataSet().getControl();
411             return myControl == null
412                     ? null
413                     : (PrometheusControlKey) myControl.getControlKey();
414         }
415 
416         /**
417          * Obtain the DataKeySet to use.
418          *
419          * @return the DataKeySet
420          */
421         private PrometheusDataKeySet getNextDataKeySet() {
422             final PrometheusControlKey myKey = getControlKey();
423             return (myKey == null)
424                     ? null
425                     : myKey.getNextDataKeySet();
426         }
427 
428         /**
429          * Update Security for items in the list.
430          *
431          * @param pReport  the report
432          * @param pControl the control key to apply
433          * @throws OceanusException on error
434          */
435         public void updateSecurity(final TethysUIThreadStatusReport pReport,
436                                    final PrometheusControlKey pControl) throws OceanusException {
437             /* Declare the new stage */
438             pReport.setNewStage(listName());
439 
440             /* Count the Number of items */
441             pReport.setNumSteps(size());
442 
443             /* Loop through the items */
444             final Iterator<T> myIterator = iterator();
445             while (myIterator.hasNext()) {
446                 final T myCurr = myIterator.next();
447 
448                 /* Only update if we are using the wrong controlKey */
449                 if (!pControl.equals(myCurr.getDataKeySet().getControlKey())) {
450                     /* Update the security */
451                     myCurr.updateSecurity(pControl.getNextDataKeySet());
452                 }
453 
454                 /* Report the progress */
455                 pReport.setNextStep();
456             }
457         }
458 
459         /**
460          * Adopt security from underlying list. If a match for the item is found in the underlying
461          * list, its security is adopted. If no match is found then the security is initialised.
462          *
463          * @param pReport the report
464          * @param pBase   The base list to adopt from
465          * @throws OceanusException on error
466          */
467         protected void adoptSecurity(final TethysUIThreadStatusReport pReport,
468                                      final PrometheusEncryptedList<?> pBase) throws OceanusException {
469             /* Declare the new stage */
470             pReport.setNewStage(listName());
471 
472             /* Count the Number of items */
473             pReport.setNumSteps(size());
474 
475             /* Obtain DataKeySet list */
476             final PrometheusDataSetCtl myData = getDataSet();
477             final PrometheusDataKeySetList mySets
478                     = myData.getDataList(PrometheusCryptographyDataType.DATAKEYSET, PrometheusDataKeySetList.class);
479 
480             /* Create an iterator for our new list */
481             final Iterator<T> myIterator = iterator();
482             final Class<T> myClass = getBaseClass();
483 
484             /* Loop through this list */
485             while (myIterator.hasNext()) {
486                 /* Locate the item in the base list */
487                 final PrometheusEncryptedDataItem myCurr = myIterator.next();
488                 final PrometheusEncryptedDataItem myBase = pBase.findItemById(myCurr.getIndexedId());
489 
490                 /* Access target correctly */
491                 final T myTarget = myClass.cast(myCurr);
492 
493                 /* If we have a base */
494                 if (myBase != null) {
495                     /* Access base correctly */
496                     final T mySource = myClass.cast(myBase);
497 
498                     /* Obtain required KeySet */
499                     PrometheusDataKeySet mySet = myBase.getDataKeySet();
500                     mySet = mySets.findItemById(mySet.getIndexedId());
501 
502                     /* Adopt the security */
503                     myTarget.adoptSecurity(mySet, mySource);
504                 } else {
505                     /* Initialise the security */
506                     myTarget.initialiseSecurity(getNextDataKeySet());
507                 }
508 
509                 /* Report the progress */
510                 pReport.setNextStep();
511             }
512         }
513     }
514 }