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.oceanus.base.OceanusException;
20  import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
21  import io.github.tonywasher.joceanus.metis.data.MetisDataDifference;
22  import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataFieldId;
23  import io.github.tonywasher.joceanus.metis.field.MetisFieldSet;
24  import io.github.tonywasher.joceanus.metis.field.MetisFieldVersionedSet;
25  import io.github.tonywasher.joceanus.metis.list.MetisListKey;
26  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataKeySet.PrometheusDataKeySetList;
27  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataSet.PrometheusCryptographyDataType;
28  import io.github.tonywasher.joceanus.tethys.api.thread.TethysUIThreadStatusReport;
29  
30  import java.util.Iterator;
31  
32  /**
33   * Encrypted Data Item and List.
34   *
35   * @author Tony Washer
36   */
37  public abstract class PrometheusEncryptedDataItem
38          extends PrometheusDataItem {
39      /**
40       * Null Encryptor.
41       */
42      private static final PrometheusEncryptor NULL_ENCRYPTOR = new PrometheusEncryptor(new OceanusDataFormatter(), null);
43  
44      /**
45       * Report fields.
46       */
47      private static final MetisFieldVersionedSet<PrometheusEncryptedDataItem> FIELD_DEFS = MetisFieldVersionedSet.newVersionedFieldSet(PrometheusEncryptedDataItem.class);
48  
49      /*
50       * FieldIds.
51       */
52      static {
53          FIELD_DEFS.declareLinkField(PrometheusCryptographyDataType.DATAKEYSET);
54      }
55  
56      /**
57       * Standard Constructor. This creates a null encryption generator. This will be overridden when
58       * a DataKeySet is assigned to the item.
59       *
60       * @param pList the list that this item is associated with
61       * @param pId   the Id of the new item (or 0 if not yet known)
62       */
63      protected PrometheusEncryptedDataItem(final PrometheusEncryptedList<?> pList,
64                                            final Integer pId) {
65          super(pList, pId);
66      }
67  
68      /**
69       * Copy Constructor. This picks up the generator from the source item.
70       *
71       * @param pList   the list that this item is associated with
72       * @param pSource the source item
73       */
74      protected PrometheusEncryptedDataItem(final PrometheusEncryptedList<?> pList,
75                                            final PrometheusEncryptedDataItem pSource) {
76          super(pList, pSource);
77      }
78  
79      /**
80       * Values Constructor. This creates a null encryption generator. This will be overridden when a
81       * ControlKey is assigned to the item.
82       *
83       * @param pList   the list that this item is associated with
84       * @param pValues the data values
85       * @throws OceanusException on error
86       */
87      protected PrometheusEncryptedDataItem(final PrometheusEncryptedList<?> pList,
88                                            final PrometheusDataValues pValues) throws OceanusException {
89          super(pList, pValues);
90  
91          /* Access dataKeySet id */
92          final Integer myId = pValues.getValue(PrometheusCryptographyDataType.DATAKEYSET, Integer.class);
93          if (myId != null) {
94              setDataKeySet(myId);
95          }
96      }
97  
98      @Override
99      protected PrometheusEncryptedValues newVersionValues() {
100         return new PrometheusEncryptedValues(this);
101     }
102 
103     @Override
104     public PrometheusEncryptedValues getValues() {
105         return (PrometheusEncryptedValues) super.getValues();
106     }
107 
108     @Override
109     public PrometheusEncryptedValues getOriginalValues() {
110         return (PrometheusEncryptedValues) super.getOriginalValues();
111     }
112 
113     /**
114      * Get the DataKeySet for this item.
115      *
116      * @return the DataKeySet
117      */
118     public final PrometheusDataKeySet getDataKeySet() {
119         return getValues().getValue(PrometheusCryptographyDataType.DATAKEYSET, PrometheusDataKeySet.class);
120     }
121 
122     /**
123      * Get the DataKeySetId for this item.
124      *
125      * @return the DataKeySetId
126      */
127     public final Integer getDataKeySetId() {
128         final PrometheusDataKeySet mySet = getDataKeySet();
129         return (mySet == null)
130                 ? null
131                 : mySet.getIndexedId();
132     }
133 
134     /**
135      * Set the DataKeySet for this item.
136      *
137      * @param pSet the dataKeySet
138      */
139     private void setValueDataKeySet(final PrometheusDataKeySet pSet) {
140         getValues().setUncheckedValue(PrometheusCryptographyDataType.DATAKEYSET, pSet);
141     }
142 
143     /**
144      * Set the KeySet id for this item.
145      *
146      * @param pId the keySet id
147      */
148     private void setValueDataKeySet(final Integer pId) {
149         getValues().setUncheckedValue(PrometheusCryptographyDataType.DATAKEYSET, pId);
150     }
151 
152     /**
153      * Get the Encryptor.
154      *
155      * @return the encryptor
156      */
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 PrometheusDataSet myData = getDataSet();
195         resolveDataLink(PrometheusCryptographyDataType.DATAKEYSET, myData.getDataKeySets());
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 PrometheusDataSet myData = getDataSet();
290         resolveDataLink(PrometheusCryptographyDataType.DATAKEYSET, myData.getDataKeySets());
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 PrometheusDataSet 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 PrometheusDataSet 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         /**
400          * Get the active controlKey.
401          *
402          * @return the active controlKey
403          */
404         private PrometheusControlKey getControlKey() {
405             final PrometheusControlData myControl = getDataSet().getControl();
406             return (myControl == null)
407                     ? null
408                     : myControl.getControlKey();
409         }
410 
411         /**
412          * Obtain the DataKeySet to use.
413          *
414          * @return the DataKeySet
415          */
416         private PrometheusDataKeySet getNextDataKeySet() {
417             final PrometheusControlKey myKey = getControlKey();
418             return (myKey == null)
419                     ? null
420                     : myKey.getNextDataKeySet();
421         }
422 
423         /**
424          * Update Security for items in the list.
425          *
426          * @param pReport  the report
427          * @param pControl the control key to apply
428          * @throws OceanusException on error
429          */
430         public void updateSecurity(final TethysUIThreadStatusReport pReport,
431                                    final PrometheusControlKey pControl) throws OceanusException {
432             /* Declare the new stage */
433             pReport.setNewStage(listName());
434 
435             /* Count the Number of items */
436             pReport.setNumSteps(size());
437 
438             /* Loop through the items */
439             final Iterator<T> myIterator = iterator();
440             while (myIterator.hasNext()) {
441                 final T myCurr = myIterator.next();
442 
443                 /* Only update if we are using the wrong controlKey */
444                 if (!pControl.equals(myCurr.getDataKeySet().getControlKey())) {
445                     /* Update the security */
446                     myCurr.updateSecurity(pControl.getNextDataKeySet());
447                 }
448 
449                 /* Report the progress */
450                 pReport.setNextStep();
451             }
452         }
453 
454         /**
455          * Adopt security from underlying list. If a match for the item is found in the underlying
456          * list, its security is adopted. If no match is found then the security is initialised.
457          *
458          * @param pReport the report
459          * @param pBase   The base list to adopt from
460          * @throws OceanusException on error
461          */
462         protected void adoptSecurity(final TethysUIThreadStatusReport pReport,
463                                      final PrometheusEncryptedList<?> pBase) throws OceanusException {
464             /* Declare the new stage */
465             pReport.setNewStage(listName());
466 
467             /* Count the Number of items */
468             pReport.setNumSteps(size());
469 
470             /* Obtain DataKeySet list */
471             final PrometheusDataSet myData = getDataSet();
472             final PrometheusDataKeySetList mySets = myData.getDataKeySets();
473 
474             /* Create an iterator for our new list */
475             final Iterator<T> myIterator = iterator();
476             final Class<T> myClass = getBaseClass();
477 
478             /* Loop through this list */
479             while (myIterator.hasNext()) {
480                 /* Locate the item in the base list */
481                 final PrometheusEncryptedDataItem myCurr = myIterator.next();
482                 final PrometheusEncryptedDataItem myBase = pBase.findItemById(myCurr.getIndexedId());
483 
484                 /* Access target correctly */
485                 final T myTarget = myClass.cast(myCurr);
486 
487                 /* If we have a base */
488                 if (myBase != null) {
489                     /* Access base correctly */
490                     final T mySource = myClass.cast(myBase);
491 
492                     /* Obtain required KeySet */
493                     PrometheusDataKeySet mySet = myBase.getDataKeySet();
494                     mySet = mySets.findItemById(mySet.getIndexedId());
495 
496                     /* Adopt the security */
497                     myTarget.adoptSecurity(mySet, mySource);
498                 } else {
499                     /* Initialise the security */
500                     myTarget.initialiseSecurity(getNextDataKeySet());
501                 }
502 
503                 /* Report the progress */
504                 pReport.setNextStep();
505             }
506         }
507     }
508 }