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.security;
18  
19  import io.github.tonywasher.joceanus.gordianknot.api.base.GordianException;
20  import io.github.tonywasher.joceanus.gordianknot.api.factory.GordianFactory;
21  import io.github.tonywasher.joceanus.gordianknot.api.factory.GordianFactory.GordianFactoryLock;
22  import io.github.tonywasher.joceanus.gordianknot.api.factory.GordianFactoryType;
23  import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianKeyPair;
24  import io.github.tonywasher.joceanus.gordianknot.api.keyset.GordianBadCredentialsException;
25  import io.github.tonywasher.joceanus.gordianknot.api.keyset.GordianKeySet;
26  import io.github.tonywasher.joceanus.gordianknot.api.lock.GordianKeyPairLock;
27  import io.github.tonywasher.joceanus.gordianknot.api.lock.GordianKeySetLock;
28  import io.github.tonywasher.joceanus.gordianknot.api.lock.GordianLockFactory;
29  import io.github.tonywasher.joceanus.gordianknot.api.lock.spec.GordianPasswordLockSpec;
30  import io.github.tonywasher.joceanus.gordianknot.api.zip.GordianZipLock;
31  import io.github.tonywasher.joceanus.gordianknot.util.GordianGenerator;
32  import io.github.tonywasher.joceanus.gordianknot.util.GordianUtilities;
33  import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
34  import io.github.tonywasher.joceanus.prometheus.exc.PrometheusDataException;
35  import io.github.tonywasher.joceanus.prometheus.exc.PrometheusLogicException;
36  import io.github.tonywasher.joceanus.prometheus.exc.PrometheusSecurityException;
37  
38  import java.nio.ByteBuffer;
39  import java.util.Arrays;
40  
41  /**
42   * Security Manager class which holds a cache of all resolved password hashes. For password
43   * hashes that were not previously resolved, previously used passwords will be attempted. If no
44   * match is found, then the user will be prompted for the password.
45   */
46  public class PrometheusSecurityPasswordManager {
47      /**
48       * Text for Bad Password Error.
49       */
50      private static final String NLS_ERRORPASS = PrometheusSecurityResource.SECURITY_BAD_PASSWORD.getValue();
51  
52      /**
53       * Security factory.
54       */
55      private final GordianFactory theFactory;
56  
57      /**
58       * Lock factory.
59       */
60      private final GordianLockFactory theLockFactory;
61  
62      /**
63       * PasswordLockSpec.
64       */
65      private final GordianPasswordLockSpec theLockSpec;
66  
67      /**
68       * The Cache.
69       */
70      private final PrometheusSecurityPasswordCache theCache;
71  
72      /**
73       * Dialog controller.
74       */
75      private PrometheusSecurityDialogController theDialog;
76  
77      /**
78       * Constructor.
79       *
80       * @param pFactory the factory
81       * @param pDialog  the dialog controller
82       * @throws OceanusException on error
83       */
84      public PrometheusSecurityPasswordManager(final GordianFactory pFactory,
85                                               final PrometheusSecurityDialogController pDialog) throws OceanusException {
86          this(pFactory, GordianUtilities.newPasswordLockSpecBuilder().passwordLock(), pDialog);
87      }
88  
89      /**
90       * Constructor.
91       *
92       * @param pFactory  the factory
93       * @param pLockSpec the lockSpec
94       * @param pDialog   the dialog controller
95       * @throws OceanusException on error
96       */
97      public PrometheusSecurityPasswordManager(final GordianFactory pFactory,
98                                               final GordianPasswordLockSpec pLockSpec,
99                                               final PrometheusSecurityDialogController pDialog) throws OceanusException {
100         /* Allocate the factory */
101         theFactory = pFactory;
102         theLockFactory = theFactory.getLockFactory();
103         theDialog = pDialog;
104         theLockSpec = pLockSpec;
105 
106         /* Allocate a new cache */
107         theCache = new PrometheusSecurityPasswordCache(this, theLockSpec);
108     }
109 
110     /**
111      * Obtain the security factory.
112      *
113      * @return the factory
114      */
115     public GordianFactory getSecurityFactory() {
116         return theFactory;
117     }
118 
119     /**
120      * Obtain the lockSpec.
121      *
122      * @return the lockSpec
123      */
124     public GordianPasswordLockSpec getLockSpec() {
125         return theLockSpec;
126     }
127 
128     /**
129      * Set the dialog controller.
130      *
131      * @param pDialog the controller
132      */
133     public void setDialogController(final PrometheusSecurityDialogController pDialog) {
134         theDialog = pDialog;
135     }
136 
137     /**
138      * Create a new factoryLock.
139      *
140      * @param pSource the description of the secured resource
141      * @return the factoryLock
142      * @throws OceanusException on error
143      */
144     public GordianFactoryLock newFactoryLock(final String pSource) throws OceanusException {
145         /* Protect against exceptions */
146         try {
147             final GordianFactory myFactory = GordianGenerator.createRandomFactory(GordianFactoryType.BC);
148             return (GordianFactoryLock) requestPassword(pSource, true, p -> createFactoryLock(myFactory, p));
149 
150         } catch (GordianException e) {
151             throw new PrometheusSecurityException(e);
152         }
153     }
154 
155     /**
156      * Create a new factoryLock.
157      *
158      * @param pFactory the factory to lock
159      * @param pSource  the description of the secured resource
160      * @return the factoryLock
161      * @throws OceanusException on error
162      */
163     public GordianFactoryLock newFactoryLock(final GordianFactory pFactory,
164                                              final String pSource) throws OceanusException {
165         return (GordianFactoryLock) requestPassword(pSource, true, p -> createFactoryLock(pFactory, p));
166     }
167 
168     /**
169      * Resolve the factoryLock bytes.
170      *
171      * @param pLockBytes the lock bytes to resolve
172      * @param pSource    the description of the secured resource
173      * @return the factoryLock
174      * @throws OceanusException on error
175      */
176     public GordianFactoryLock resolveFactoryLock(final byte[] pLockBytes,
177                                                  final String pSource) throws OceanusException {
178         /* Look up resolved factory */
179         GordianFactoryLock myFactory = theCache.lookUpResolvedFactoryLock(pLockBytes);
180 
181         /* If we have not seen the lock then attempt known passwords */
182         if (myFactory == null) {
183             myFactory = theCache.attemptKnownPasswordsForFactoryLock(pLockBytes);
184         }
185 
186         /* If we have not resolved the lock */
187         if (myFactory == null) {
188             myFactory = (GordianFactoryLock) requestPassword(pSource, false, p -> resolveFactoryLock(pLockBytes, p));
189         }
190 
191         /* Return the resolved factoryLock */
192         return myFactory;
193     }
194 
195     /**
196      * obtain new locked factory (same password).
197      *
198      * @param pReference the reference to clone password from
199      * @return the similar factoryLock
200      * @throws OceanusException on error
201      */
202     public GordianFactoryLock similarFactoryLock(final Object pReference) throws OceanusException {
203         /* Protect against exceptions */
204         try {
205             /* Create a new random factory */
206             final GordianFactory myFactory = GordianGenerator.createRandomFactory(GordianFactoryType.BC);
207 
208             /* LookUp the password */
209             final ByteBuffer myPassword = theCache.lookUpResolvedPassword(pReference);
210 
211             /* Create a similar factoryLock */
212             return theCache.createSimilarFactoryLock(myFactory, myPassword);
213 
214         } catch (GordianException e) {
215             throw new PrometheusSecurityException(e);
216         }
217     }
218 
219     /**
220      * Create a new keySetLock.
221      *
222      * @param pSource the description of the secured resource
223      * @return the keySetLock
224      * @throws OceanusException on error
225      */
226     public GordianKeySetLock newKeySetLock(final String pSource) throws OceanusException {
227         /* Protect against exceptions */
228         try {
229             final GordianKeySet myKeySet = theFactory.getKeySetFactory().generateKeySet(theLockSpec.getKeySetSpec());
230             return (GordianKeySetLock) requestPassword(pSource, true, p -> createKeySetLock(myKeySet, p));
231 
232         } catch (GordianException e) {
233             throw new PrometheusSecurityException(e);
234         }
235     }
236 
237     /**
238      * Create a new keySetLock.
239      *
240      * @param pKeySet the keySet to lock
241      * @param pSource the description of the secured resource
242      * @return the keySetLock
243      * @throws OceanusException on error
244      */
245     public GordianKeySetLock newKeySetLock(final GordianKeySet pKeySet,
246                                            final String pSource) throws OceanusException {
247         return (GordianKeySetLock) requestPassword(pSource, true, p -> createKeySetLock(pKeySet, p));
248     }
249 
250     /**
251      * Resolve the keySetLock bytes.
252      *
253      * @param pLockBytes the lock bytes to resolve
254      * @param pSource    the description of the secured resource
255      * @return the keySetLock
256      * @throws OceanusException on error
257      */
258     public GordianKeySetLock resolveKeySetLock(final byte[] pLockBytes,
259                                                final String pSource) throws OceanusException {
260         /* Look up resolved keySet */
261         GordianKeySetLock myKeySet = theCache.lookUpResolvedKeySetLock(pLockBytes);
262 
263         /* If we have not seen the lock then attempt known passwords */
264         if (myKeySet == null) {
265             myKeySet = theCache.attemptKnownPasswordsForKeySetLock(pLockBytes);
266         }
267 
268         /* If we have not resolved the lock */
269         if (myKeySet == null) {
270             myKeySet = (GordianKeySetLock) requestPassword(pSource, false, p -> resolveKeySetLock(pLockBytes, p));
271         }
272 
273         /* Return the resolved keySetLock */
274         return myKeySet;
275     }
276 
277     /**
278      * obtain new locked keySet (same password).
279      *
280      * @param pReference the reference to clone password from
281      * @return the similar keySetLock
282      * @throws OceanusException on error
283      */
284     public GordianKeySetLock similarKeySetLock(final Object pReference) throws OceanusException {
285         /* Protect against exceptions */
286         try {
287             /* Create a new random keySet */
288             final GordianKeySet myKeySet = theFactory.getKeySetFactory().generateKeySet(theLockSpec.getKeySetSpec());
289 
290             /* LookUp the password */
291             final ByteBuffer myPassword = theCache.lookUpResolvedPassword(pReference);
292 
293             /* Create a similar keySetLock */
294             return theCache.createSimilarKeySetLock(myKeySet, myPassword);
295 
296         } catch (GordianException e) {
297             throw new PrometheusSecurityException(e);
298         }
299     }
300 
301     /**
302      * Create a new keyPairLock.
303      *
304      * @param pKeyPair the keyPair
305      * @param pSource  the description of the secured resource
306      * @return the keyPairLock
307      * @throws OceanusException on error
308      */
309     public GordianKeyPairLock newKeyPairLock(final GordianKeyPair pKeyPair,
310                                              final String pSource) throws OceanusException {
311         return (GordianKeyPairLock) requestPassword(pSource, true, p -> createKeyPairLock(pKeyPair, p));
312     }
313 
314     /**
315      * Resolve the keyPairLock.
316      *
317      * @param pLockBytes the LockBytes to resolve
318      * @param pKeyPair   the keyPair
319      * @param pSource    the description of the secured resource
320      * @return the keyPairLock
321      * @throws OceanusException on error
322      */
323     public GordianKeyPairLock resolveKeyPairLock(final byte[] pLockBytes,
324                                                  final GordianKeyPair pKeyPair,
325                                                  final String pSource) throws OceanusException {
326         /* Look up resolved keySet */
327         GordianKeyPairLock myKeyPair = theCache.lookUpResolvedKeyPairLock(pLockBytes, pKeyPair);
328 
329         /* If we have not seen the lock then attempt known passwords */
330         if (myKeyPair == null) {
331             myKeyPair = theCache.attemptKnownPasswordsForKeyPairLock(pLockBytes, pKeyPair);
332         }
333 
334         /* If we have not resolved the lock */
335         if (myKeyPair == null) {
336             myKeyPair = (GordianKeyPairLock) requestPassword(pSource, false, p -> resolveKeyPairLock(pLockBytes, pKeyPair, p));
337         }
338 
339         /* Return the resolved keyPairLock */
340         return myKeyPair;
341     }
342 
343     /**
344      * obtain similar (same password) zipLock.
345      *
346      * @param pKeyPair   the keyPair
347      * @param pReference the reference to clone password from
348      * @return the similar keyPairLock
349      * @throws OceanusException on error
350      */
351     public GordianKeyPairLock similarKeyPairLock(final GordianKeyPair pKeyPair,
352                                                  final Object pReference) throws OceanusException {
353         /* LookUp the password */
354         final ByteBuffer myPassword = theCache.lookUpResolvedPassword(pReference);
355 
356         /* Create a similar keyPairLock */
357         return theCache.createSimilarKeyPairLock(pKeyPair, myPassword);
358     }
359 
360     /**
361      * Resolve the zipLock.
362      *
363      * @param pZipLock the hash bytes to resolve
364      * @param pSource  the description of the secured resource
365      * @throws OceanusException on error
366      */
367     public void resolveZipLock(final GordianZipLock pZipLock,
368                                final String pSource) throws OceanusException {
369         switch (pZipLock.getLockType()) {
370             case KEYSET_PASSWORD:
371                 resolveKeySetZipLock(pZipLock, pSource);
372                 break;
373             case FACTORY_PASSWORD:
374                 resolveFactoryZipLock(pZipLock, pSource);
375                 break;
376             case KEYPAIR_PASSWORD:
377             default:
378                 throw new PrometheusLogicException("KeyPair zipLock not supported yet");
379         }
380     }
381 
382     /**
383      * Resolve a keySet ZipLock.
384      *
385      * @param pZipLock the zipLock
386      * @param pSource  the description of the secured resource
387      * @throws OceanusException on error
388      */
389     private void resolveKeySetZipLock(final GordianZipLock pZipLock,
390                                       final String pSource) throws OceanusException {
391         /* Protect against exceptions */
392         try {
393             /* Access lockBytes */
394             final byte[] myLockBytes = pZipLock.getLockBytes();
395 
396             /* Look up resolved keySet */
397             final GordianKeySetLock myLock = resolveKeySetLock(myLockBytes, pSource);
398 
399             /* If we resolved the lock */
400             if (myLock != null) {
401                 pZipLock.unlock(myLock);
402             }
403 
404         } catch (GordianException e) {
405             throw new PrometheusSecurityException(e);
406         }
407     }
408 
409     /**
410      * Resolve a factory ZipLock.
411      *
412      * @param pZipLock the zipLock
413      * @param pSource  the description of the secured resource
414      * @throws OceanusException on error
415      */
416     private void resolveFactoryZipLock(final GordianZipLock pZipLock,
417                                        final String pSource) throws OceanusException {
418         /* Protect against exceptions */
419         try {
420             /* Access lockBytes */
421             final byte[] myLockBytes = pZipLock.getLockBytes();
422 
423             /* Look up resolved keySet */
424             final GordianFactoryLock myLock = resolveFactoryLock(myLockBytes, pSource);
425 
426             /* If we resolved the lock */
427             if (myLock != null) {
428                 pZipLock.unlock(myLock);
429             }
430 
431         } catch (GordianException e) {
432             throw new PrometheusSecurityException(e);
433         }
434     }
435 
436     /**
437      * Request password.
438      *
439      * @param pSource      the description of the secured resource
440      * @param pNeedConfirm do we need confirmation
441      * @param pProcessor   the password processor
442      * @return the keySetHash
443      * @throws OceanusException on error
444      */
445     public Object requestPassword(final String pSource,
446                                   final boolean pNeedConfirm,
447                                   final PrometheusProcessPassword pProcessor) throws OceanusException {
448         /* Allocate variables */
449         Object myResult = null;
450 
451         /* Create a new password dialog */
452         theDialog.createTheDialog(pSource, pNeedConfirm);
453 
454         /* Prompt for the password */
455         boolean isPasswordOk = false;
456         char[] myPassword = null;
457         while (theDialog.showTheDialog()) {
458             try {
459                 /* Access the password */
460                 myPassword = theDialog.getPassword();
461 
462                 /* Validate the password */
463                 final String myError = PrometheusPassCheck.validatePassword(myPassword);
464                 if (myError != null) {
465                     theDialog.reportBadPassword(myError);
466                     continue;
467                 }
468 
469                 /* Process the password */
470                 theDialog.showTheSpinner(true);
471                 myResult = pProcessor.processPassword(myPassword);
472 
473                 /* No exception so we are good to go */
474                 isPasswordOk = true;
475                 break;
476 
477             } catch (GordianBadCredentialsException e) {
478                 theDialog.reportBadPassword(NLS_ERRORPASS);
479                 if (myPassword != null) {
480                     Arrays.fill(myPassword, (char) 0);
481                 }
482 
483             } finally {
484                 if (myPassword != null) {
485                     Arrays.fill(myPassword, (char) 0);
486                     myPassword = null;
487                 }
488             }
489         }
490 
491         /* release password resources */
492         theDialog.releaseDialog();
493 
494         /* If we did not get a password */
495         if (!isPasswordOk) {
496             /* Throw an exception */
497             throw new PrometheusDataException(NLS_ERRORPASS);
498         }
499 
500         /* Return the result */
501         return myResult;
502     }
503 
504     /**
505      * Process password interface.
506      */
507     @FunctionalInterface
508     public interface PrometheusProcessPassword {
509         /**
510          * Process password.
511          *
512          * @param pPassword the password
513          * @return the result
514          * @throws OceanusException               on error
515          * @throws GordianBadCredentialsException if password does not match
516          */
517         Object processPassword(char[] pPassword) throws OceanusException;
518     }
519 
520     /**
521      * Create new factoryLock.
522      *
523      * @param pFactory  the factory
524      * @param pPassword the password
525      * @return the new lock
526      * @throws OceanusException on error
527      */
528     private GordianFactoryLock createFactoryLock(final GordianFactory pFactory,
529                                                  final char[] pPassword) throws OceanusException {
530         /* Protect against exceptions */
531         try {
532             final GordianFactoryLock myLock = theFactory.newFactoryLock(pFactory, theLockSpec, pPassword);
533             theCache.addResolvedFactory(myLock, pPassword);
534             return myLock;
535 
536         } catch (GordianException e) {
537             throw new PrometheusSecurityException(e);
538         }
539     }
540 
541     /**
542      * Resolve password for factoryLock.
543      *
544      * @param pLockBytes the lock bytes
545      * @param pPassword  the password
546      * @return the resolved lock
547      * @throws OceanusException on error
548      */
549     private GordianFactoryLock resolveFactoryLock(final byte[] pLockBytes,
550                                                   final char[] pPassword) throws OceanusException {
551         /* Protect against exceptions */
552         try {
553             final GordianFactoryLock myFactory = theFactory.resolveFactoryLock(pLockBytes, pPassword);
554             theCache.addResolvedFactory(myFactory, pPassword);
555             return myFactory;
556 
557         } catch (GordianException e) {
558             throw new PrometheusSecurityException(e);
559         }
560     }
561 
562     /**
563      * Create new keySetLock.
564      *
565      * @param pKeySet   the keySet
566      * @param pPassword the password
567      * @return the new lock
568      * @throws OceanusException on error
569      */
570     private GordianKeySetLock createKeySetLock(final GordianKeySet pKeySet,
571                                                final char[] pPassword) throws OceanusException {
572         /* Protect against exceptions */
573         try {
574             final GordianKeySetLock myLock = theLockFactory.newKeySetLock(pKeySet, theLockSpec, pPassword);
575             theCache.addResolvedKeySet(myLock, pPassword);
576             return myLock;
577 
578         } catch (GordianException e) {
579             throw new PrometheusSecurityException(e);
580         }
581     }
582 
583     /**
584      * Resolve password for keySetLock.
585      *
586      * @param pLockBytes the lock bytes
587      * @param pPassword  the password
588      * @return the resolved lock
589      * @throws OceanusException on error
590      */
591     private GordianKeySetLock resolveKeySetLock(final byte[] pLockBytes,
592                                                 final char[] pPassword) throws OceanusException {
593         /* Protect against exceptions */
594         try {
595             final GordianKeySetLock myKeySet = theLockFactory.resolveKeySetLock(pLockBytes, pPassword);
596             theCache.addResolvedKeySet(myKeySet, pPassword);
597             return myKeySet;
598 
599         } catch (GordianException e) {
600             throw new PrometheusSecurityException(e);
601         }
602     }
603 
604     /**
605      * Create new keyPairLock.
606      *
607      * @param pKeyPair  the keyPair
608      * @param pPassword the password
609      * @return the new lock
610      * @throws OceanusException on error
611      */
612     private GordianKeyPairLock createKeyPairLock(final GordianKeyPair pKeyPair,
613                                                  final char[] pPassword) throws OceanusException {
614         /* Protect against exceptions */
615         try {
616             final GordianKeyPairLock myLock = theLockFactory.newKeyPairLock(theLockSpec, pKeyPair, pPassword);
617             theCache.addResolvedKeyPair(myLock, pPassword);
618             return myLock;
619 
620         } catch (GordianException e) {
621             throw new PrometheusSecurityException(e);
622         }
623     }
624 
625     /**
626      * Resolve password for keyPairLock.
627      *
628      * @param pLockBytes the lock bytes
629      * @param pKeyPair   the keyPair
630      * @param pPassword  the password
631      * @return the resolved lock
632      * @throws OceanusException on error
633      */
634     private GordianKeyPairLock resolveKeyPairLock(final byte[] pLockBytes,
635                                                   final GordianKeyPair pKeyPair,
636                                                   final char[] pPassword) throws OceanusException {
637         /* Protect against exceptions */
638         try {
639             final GordianKeyPairLock myKeyPair = theLockFactory.resolveKeyPairLock(pLockBytes, pKeyPair, pPassword);
640             theCache.addResolvedKeyPair(myKeyPair, pPassword);
641             return myKeyPair;
642 
643         } catch (GordianException e) {
644             throw new PrometheusSecurityException(e);
645         }
646     }
647 
648     /**
649      * Password Check enum.
650      */
651     private enum PrometheusPassCheck {
652         /**
653          * Numeric.
654          */
655         NUMERIC(1),
656 
657         /**
658          * Lowercase.
659          */
660         LOWERCASE(2),
661 
662         /**
663          * Numeric.
664          */
665         UPPERCASE(4),
666 
667         /**
668          * Special.
669          */
670         SPECIAL(8);
671 
672         /**
673          * Text for Bad Length Error.
674          */
675         private static final String NLS_BADLENGTH = PrometheusSecurityResource.SECURITY_BAD_PASSLEN.getValue();
676 
677         /**
678          * Text for Invalid characters Error.
679          */
680         private static final String NLS_BADCHAR = PrometheusSecurityResource.SECURITY_INVALID_CHARS.getValue();
681 
682         /**
683          * Special characters.
684          */
685         private static final String SPECIAL_CHARS = "%$^!@-_+~#&*";
686 
687         /**
688          * Minimum password length.
689          */
690         private static final int MINPASSLEN = 8;
691 
692         /**
693          * The flag.
694          */
695         private final int theFlag;
696 
697         /**
698          * Constructor.
699          *
700          * @param pFlag the flag
701          */
702         PrometheusPassCheck(final int pFlag) {
703             theFlag = pFlag;
704         }
705 
706         /**
707          * Obtain the flag.
708          *
709          * @return the flag
710          */
711         private int getFlag() {
712             return theFlag;
713         }
714 
715         /**
716          * Check password.
717          *
718          * @param pPassword the password
719          * @return the error message (or null)
720          */
721         static String validatePassword(final char[] pPassword) {
722             /* Password must be at least 8 characters in length */
723             if (pPassword.length < MINPASSLEN) {
724                 return NLS_BADLENGTH;
725             }
726 
727             /* Loop through the password ensuring that it has at least one of each type */
728             int myResult = 0;
729             for (char c : pPassword) {
730                 if (Character.isDigit(c)) {
731                     myResult |= NUMERIC.getFlag();
732                 } else if (Character.isLowerCase(c)) {
733                     myResult |= LOWERCASE.getFlag();
734                 } else if (Character.isUpperCase(c)) {
735                     myResult |= UPPERCASE.getFlag();
736                 } else if (SPECIAL_CHARS.indexOf(c) != -1) {
737                     myResult |= SPECIAL.getFlag();
738                 }
739             }
740 
741             /* If we do not have at least one of each */
742             if (myResult != getExpectedResult()) {
743                 return NLS_BADCHAR;
744             }
745             return null;
746         }
747 
748         /**
749          * Obtain expected result.
750          *
751          * @return the expected result
752          */
753         private static int getExpectedResult() {
754             int myResult = 0;
755             for (PrometheusPassCheck myCheck : values()) {
756                 myResult |= myCheck.getFlag();
757             }
758             return myResult;
759         }
760     }
761 }