Port to java 17 06/135106/2
authorDan Timoney <dtimoney@att.com>
Mon, 19 Jun 2023 20:05:33 +0000 (16:05 -0400)
committerDan Timoney <dtimoney@att.com>
Thu, 10 Aug 2023 18:48:32 +0000 (14:48 -0400)
Update to java 17 / springboot 3 to align with OpenDaylight Argon.
Copied and ported CADI library from AAF

Issue-ID: CCSDK-3917
Signed-off-by: Dan Timoney <dtimoney@att.com>
Change-Id: Idecb0cf43c48ccbbc0c61bf4278b87a37f92a56e

226 files changed:
cadi/core/conf/cadi.properties [new file with mode: 0644]
cadi/core/pom.xml [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/AES.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/AbsUserCache.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Access.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/BasicCred.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/BufferedServletInputStream.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/CachedPrincipal.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/CachingLur.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/CadiException.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/CadiWrap.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Capacitor.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/CmdLine.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Connector.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/CredVal.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/CredValDomain.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/GetCred.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Hash.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Locator.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/LocatorException.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Lur.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Permission.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/PropAccess.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Revalidator.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/SecuritySetter.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/ServletContextAccess.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Symm.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Taf.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Transmutate.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/TrustChecker.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/User.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/UserChain.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/Config.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/Get.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/GetAccess.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/MultiGet.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/RegistrationPropHolder.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/SecurityInfo.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/SecurityInfoC.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/SecurityInfoInit.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/UsersDump.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/AUTHZ.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/AUTHZServlet.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/AccessGetter.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/CadiApiEnforcementFilter.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/CadiFilter.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/CadiHTTPManip.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/FCGet.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/MapBathConverter.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/MapPermConverter.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/NullPermConverter.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/PathFilter.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/PermConverter.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/RolesAllowed.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/ServletImpl.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/SideChain.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/lur/ConfigPrincipal.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/lur/EpiLur.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/lur/LocalLur.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/lur/LocalPermission.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/lur/NullLur.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/BasicPrincipal.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/BearerPrincipal.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/CachedBasicPrincipal.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/Kind.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/OAuth2FormPrincipal.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/StringTagLookup.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/TaggedPrincipal.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/TrustPrincipal.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/UnAuthPrincipal.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/X509Principal.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/AbsTafResp.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/EpiTaf.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/HttpEpiTaf.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/HttpTaf.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/LoginPageTafResp.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/NullTaf.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/NullTafResp.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/PuntTafResp.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/Redirectable.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/TafResp.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/TrustNotTafResp.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/TrustTafResp.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/basic/BasicHttpTaf.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/basic/BasicHttpTafResp.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/cert/CertIdentity.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/cert/X509HttpTafResp.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/cert/X509Taf.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/dos/DenialOfServiceTaf.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/dos/DenialOfServiceTafResp.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/CSV.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/Chmod.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/FQI.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/FixURIinfo.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/Holder.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/JsonOutputStream.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/Log.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/MaskFormatException.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/MyConsole.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/NetMask.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/Pool.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/Split.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/SubStandardConsole.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/TheConsole.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/Timing.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/UserChainManip.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/Vars.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/wsse/Action.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/wsse/Match.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/wsse/WSSEParser.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/wsse/XEvent.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/wsse/XReader.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/config/test/JU_Get.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/config/test/JU_GetAccess.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/config/test/JU_MapBathConverter.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/config/test/JU_MultiGet.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/config/test/JU_RegistrationPropHolder.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/config/test/JU_SecurityInfo.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/config/test/JU_SecurityInfoC.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/config/test/JU_UsersDump.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/filter/test/JU_AUTHZServlet.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/filter/test/JU_AccessGetter.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/filter/test/JU_MapPermConverter.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/filter/test/JU_NullPermConverter.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/filter/test/JU_PathFilter.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/lur/test/JU_ConfigPrincipal.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/lur/test/JU_EpiLur.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/lur/test/JU_LocalLur.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/lur/test/JU_LocalPermission.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/lur/test/JU_NullLur.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_BasicPrincipal.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_CachedBasicPrincipal.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_Kind.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_OAuth2FormPrincipal.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_StringTagLookup.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_TaggedPrincipal.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_TrustPrincipal.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_UnAuthPrincipal.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_X509Principal.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/basic/test/JU_BasicHttpTaf.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/basic/test/JU_BasicHttpTafResp.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/cert/test/JU_X509HttpTafResp.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/dos/test/JU_DenialOfServiceTaf.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/dos/test/JU_DenialOfServiceTafResp.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/test/JU_AbsTafResp.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/test/JU_EpiTaf.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/test/JU_HttpEpiTaf.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/test/JU_LoginPageTafResp.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/test/JU_NullTaf.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/test/JU_PuntTafResp.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/test/JU_TrustNotTafResp.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/test/JU_TrustTafResp.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_AES.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_AbsUserCache.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_Access.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_Base64.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_BufferedCadiWrap.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_BufferedServletInputStream.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_CadiException.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_CadiWrap.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_Capacitor.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_CmdLine.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_Hash.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_LocatorException.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_PropAccess.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_ServletContextAccess.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_Symm.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_TrustChecker.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_User.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_CSV.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_Chmod.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_FQI.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_Holder.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_JsonOutputStream.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_MaskFormatException.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_NetMask.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_Pool.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_Split.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_SubStandardConsole.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_TheConsole.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_UserChainManip.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_Vars.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/wsse/test/JU_WSSEParser.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/wsse/test/JU_XReader.java [new file with mode: 0644]
cadi/core/src/test/resources/AESKeyFile [new file with mode: 0644]
cadi/core/src/test/resources/CBUSevent.xml [new file with mode: 0644]
cadi/core/src/test/resources/keyfile [new file with mode: 0644]
cadi/pom.xml [new file with mode: 0755]
ms/neng/pom.xml
ms/neng/src/main/java/org/onap/ccsdk/apps/ms/neng/core/WebConfiguration.java
ms/neng/src/main/java/org/onap/ccsdk/apps/ms/neng/core/service/SpringServiceImpl.java
ms/neng/src/main/java/org/onap/ccsdk/apps/ms/neng/persistence/entity/ExternalInterface.java
ms/neng/src/main/java/org/onap/ccsdk/apps/ms/neng/persistence/entity/GeneratedName.java
ms/neng/src/main/java/org/onap/ccsdk/apps/ms/neng/persistence/entity/IdentifierMap.java
ms/neng/src/main/java/org/onap/ccsdk/apps/ms/neng/persistence/entity/PolicyDetails.java
ms/neng/src/main/java/org/onap/ccsdk/apps/ms/neng/persistence/entity/ServiceParameter.java
ms/neng/src/main/java/org/onap/ccsdk/apps/ms/neng/service/extinf/impl/AaiServiceImpl.java
ms/neng/src/main/java/org/onap/ccsdk/apps/ms/neng/service/extinf/impl/PolicyFinderServiceImpl.java
ms/sliboot/pom.xml
ms/sliboot/src/main/java/org/onap/ccsdk/apps/ms/sliboot/FilterConfiguration.java
ms/sliboot/src/main/java/org/onap/ccsdk/apps/ms/sliboot/SlibootApp.java
ms/sliboot/src/main/java/org/onap/ccsdk/apps/ms/sliboot/controllers/RestconfApiController.java
ms/sliboot/src/main/java/org/onap/ccsdk/apps/ms/sliboot/data/TestResultConfig.java
ms/sliboot/src/main/java/org/onap/ccsdk/apps/ms/sliboot/data/TestResultOperational.java
ms/sliboot/src/main/templates/api.mustache
ms/sliboot/src/main/templates/apiOriginFilter.mustache [new file with mode: 0644]
ms/sliboot/src/main/templates/libraries/spring-boot/swagger2SpringBoot.mustache [new file with mode: 0644]
ms/sliboot/src/main/templates/libraries/spring-mvc/swaggerUiConfiguration.mustache [new file with mode: 0644]
ms/vlantag-api/pom.xml
ms/vlantag-api/src/main/java/org/onap/ccsdk/apps/ms/vlantagapi/core/ApplicationSecurityConfig.java
ms/vlantag-api/src/main/java/org/onap/ccsdk/apps/ms/vlantagapi/core/model/AssignVlanTagRequest.java
ms/vlantag-api/src/main/java/org/onap/ccsdk/apps/ms/vlantagapi/core/model/AssignVlanTagRequestInput.java
ms/vlantag-api/src/main/java/org/onap/ccsdk/apps/ms/vlantagapi/core/model/AssignVlanTagResponse.java
ms/vlantag-api/src/main/java/org/onap/ccsdk/apps/ms/vlantagapi/core/model/AssignVlanTagResponseOutput.java
ms/vlantag-api/src/main/java/org/onap/ccsdk/apps/ms/vlantagapi/core/model/UnassignVlanTagRequest.java
ms/vlantag-api/src/main/java/org/onap/ccsdk/apps/ms/vlantagapi/core/model/UnassignVlanTagRequestInput.java
ms/vlantag-api/src/main/java/org/onap/ccsdk/apps/ms/vlantagapi/core/model/UnassignVlanTagResponse.java
ms/vlantag-api/src/main/java/org/onap/ccsdk/apps/ms/vlantagapi/core/model/UnassignVlanTagResponseOutput.java
ms/vlantag-api/src/main/java/org/onap/ccsdk/apps/ms/vlantagapi/core/model/VlanTag.java
ms/vlantag-api/src/main/java/org/onap/ccsdk/apps/ms/vlantagapi/core/service/VlantagApiService.java
pom.xml
services/pom.xml
services/src/main/java/org/onap/ccsdk/apps/filters/ContentTypeFilter.java
services/src/main/java/org/onap/ccsdk/apps/filters/PayloadLoggingFilter.java
services/src/main/java/org/onap/ccsdk/apps/services/RestExceptionHandler.java
services/src/test/java/org/onap/ccsdk/apps/services/RestExceptionHandlerTest.java

diff --git a/cadi/core/conf/cadi.properties b/cadi/core/conf/cadi.properties
new file mode 100644 (file)
index 0000000..769356f
--- /dev/null
@@ -0,0 +1,53 @@
+#########
+#  ============LICENSE_START====================================================
+#  org.onap.aaf
+#  ===========================================================================
+#  Copyright (c) 2017 AT&T Intellectual Property. All rights reserved.
+#  ===========================================================================
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#  ============LICENSE_END====================================================
+#
+# This is a normal Java Properties File
+# Comments are with Pound Signs at beginning of lines,
+# and multi-line expression of properties can be obtained by backslash at end of line
+
+# Certain machines have several possible machine names, and
+# the right one may not be reported.  This is especially
+# important for CSP Authorization, which will only 
+# function on official AT&T domains.
+hostname=veeger.mo.sbc.com 
+
+port=2533
+
+# CSP has Production mode (active users) or DEVL mode (for 
+# Testing purposes... Bogus users)
+#csp_domain=DEVL
+csp_domain=PROD
+
+# Report all AUTHN and AUTHZ activity
+loglevel=AUDIT
+
+#
+# BasicAuth and other User/Password support
+#
+# The realm reported on BasicAuth callbacks
+basic_realm=spiderman.agile.att.com
+users=ks%xiVUs_25_1jqGdJ24hqy43Gi;
+groups=aaf:Jd8bb3jslg88b@spiderman.agile.att.com%7sZCPBZ_8iWbslqdjWFIDLgTZlm9ung0ym-G,\
+               jg1555,lg2384,rd8227,tp007s,pe3617;
+       
+
+# Keyfile (with relative path) for encryption.  This file
+# should be marked as ReadOnly by Only the running process
+# for security's sake
+keyfile=conf/keyfile
diff --git a/cadi/core/pom.xml b/cadi/core/pom.xml
new file mode 100644 (file)
index 0000000..7ee4c22
--- /dev/null
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.onap.ccsdk.apps</groupId>
+        <artifactId>ccsdk-apps-cadi</artifactId>
+        <version>1.6.0-SNAPSHOT</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <name>CCSDK port of CADI Core Framework</name>
+    <artifactId>ccsdk-apps-cadi-core</artifactId>
+    <packaging>jar</packaging>
+
+    <properties>
+    <!--  SONAR  -->
+        <jacoco.version>0.7.7.201606060606</jacoco.version>
+        <powermock.version>1.7.4</powermock.version>
+        <sonar-jacoco-listeners.version>3.2</sonar-jacoco-listeners.version>
+        <sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin>
+        <!-- Default Sonar configuration -->
+        <sonar.jacoco.reportPaths>target/code-coverage/jacoco-ut.exec</sonar.jacoco.reportPaths>
+        <sonar.jacoco.itReportPaths>target/code-coverage/jacoco-it.exec</sonar.jacoco.itReportPaths>
+        <!-- Note: This list should match jacoco-maven-plugin's exclusion list below -->
+        <sonar.exclusions>**/gen/**,**/generated-sources/**,**/yang-gen**,**/pax/**</sonar.exclusions>
+        <nexusproxy>https://nexus.onap.org</nexusproxy>
+        <snapshotNexusPath>/content/repositories/snapshots/</snapshotNexusPath>
+        <releaseNexusPath>/content/repositories/releases/</releaseNexusPath>
+        <stagingNexusPath>/content/repositories/staging/</stagingNexusPath>
+        <sitePath>/content/sites/site/org/onap/aaf/authz/${project.artifactId}/${project.version}</sitePath>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>jakarta.servlet</groupId>
+            <artifactId>jakarta.servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-all</artifactId>
+            <version>1.3</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-module-junit4</artifactId>
+            <version>${powermock.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-api-mockito</artifactId>
+            <version>${powermock.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.jacoco</groupId>
+                <artifactId>jacoco-maven-plugin</artifactId>
+                <configuration>
+                    <excludes>
+                        <exclude>**/gen/**</exclude>
+                        <exclude>**/generated-sources/**</exclude>
+                        <exclude>**/yang-gen/**</exclude>
+                        <exclude>**/pax/**</exclude>
+                    </excludes>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>pre-unit-test</id>
+                        <goals>
+                            <goal>prepare-agent</goal>
+                        </goals>
+                        <configuration>
+                            <destFile>${project.build.directory}/code-coverage/jacoco-ut.exec</destFile>
+                            <propertyName>surefireArgLine</propertyName>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>post-unit-test</id>
+                        <phase>test</phase>
+                        <goals>
+                            <goal>report</goal>
+                        </goals>
+                        <configuration>
+                            <dataFile>${project.build.directory}/code-coverage/jacoco-ut.exec</dataFile>
+                            <outputDirectory>${project.reporting.outputDirectory}/jacoco-ut</outputDirectory>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>pre-integration-test</id>
+                        <phase>pre-integration-test</phase>
+                        <goals>
+                            <goal>prepare-agent</goal>
+                        </goals>
+                        <configuration>
+                            <destFile>${project.build.directory}/code-coverage/jacoco-it.exec</destFile>
+                            <propertyName>failsafeArgLine</propertyName>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>post-integration-test</id>
+                        <phase>post-integration-test</phase>
+                        <goals>
+                            <goal>report</goal>
+                        </goals>
+                        <configuration>
+                            <dataFile>${project.build.directory}/code-coverage/jacoco-it.exec</dataFile>
+                            <outputDirectory>${project.reporting.outputDirectory}/jacoco-it</outputDirectory>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/AES.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/AES.java
new file mode 100644 (file)
index 0000000..55e148a
--- /dev/null
@@ -0,0 +1,131 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.onap.ccsdk.apps.cadi.Symm.Encryption;
+import org.onap.ccsdk.apps.cadi.util.Chmod;
+
+
+/**
+ * AES Class wraps Cipher AES, 128
+ * NOTE: While not explicitly stated in JavaDocs, Ciphers AND SecretKeySpecs are NOT ThreadSafe
+ * Ciphers take time to create, therefore, we have pooled them.
+ *
+ * @author Jonathan
+ *
+ */
+public class AES implements Encryption {
+    public static final String AES = AES.class.getSimpleName();
+    public static final int AES_KEY_SIZE = 128; // 256 isn't supported on all JDKs.
+
+    private SecretKeySpec aeskeySpec;
+
+    public static SecretKey newKey() throws NoSuchAlgorithmException {
+        KeyGenerator kgen = KeyGenerator.getInstance(AES);
+        kgen.init(AES_KEY_SIZE);
+        return kgen.generateKey();
+    }
+
+    public AES(byte[] aeskey, int offset, int len){
+        aeskeySpec = new SecretKeySpec(aeskey,offset,len,AES);
+    }
+
+    public byte[] encrypt(byte[] in) throws CadiException {
+        try {
+            Cipher c = Cipher.getInstance(AES);
+            c.init(Cipher.ENCRYPT_MODE,aeskeySpec);
+            return c.doFinal(in);
+        } catch (InvalidKeyException | IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | NoSuchPaddingException e) {
+            throw new CadiException(e);
+        }
+    }
+
+    public byte[] decrypt(byte[] in) throws CadiException {
+        try {
+            Cipher c = Cipher.getInstance(AES);
+            c.init(Cipher.DECRYPT_MODE,aeskeySpec);
+            return c.doFinal(in);
+        } catch (InvalidKeyException | IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | NoSuchPaddingException e) {
+            throw new CadiException(e);
+        }
+    }
+
+    public void save(File keyfile) throws IOException {
+        FileOutputStream fis = new FileOutputStream(keyfile);
+        try {
+            fis.write(aeskeySpec.getEncoded());
+        } finally {
+            fis.close();
+        }
+        Chmod.to400.chmod(keyfile);
+    }
+
+    public CipherOutputStream outputStream(OutputStream os, boolean encrypt) {
+        try {
+            Cipher c = Cipher.getInstance(AES);
+            if (encrypt) {
+                c.init(Cipher.ENCRYPT_MODE,aeskeySpec);
+            } else {
+                c.init(Cipher.DECRYPT_MODE,aeskeySpec);
+            }
+            return new CipherOutputStream(os,c);
+        } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException e) {
+            // Cannot add Exception to this API.  throw Runtime
+            System.err.println("Error creating Aes CipherOutputStream");
+            return null;  // should never get here.
+        }
+    }
+
+    public CipherInputStream inputStream(InputStream is, boolean encrypt) {
+        try {
+            Cipher c = Cipher.getInstance(AES);
+            if (encrypt) {
+                c.init(Cipher.ENCRYPT_MODE,aeskeySpec);
+            } else {
+                c.init(Cipher.DECRYPT_MODE,aeskeySpec);
+            }
+            return new CipherInputStream(is,c);
+        } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException e) {
+            // Cannot add Exception to this API.  throw Runtime
+            System.err.println("Error creating Aes CipherInputStream");
+            return null;  // should never get here.
+        }
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/AbsUserCache.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/AbsUserCache.java
new file mode 100644 (file)
index 0000000..b8fd7e4
--- /dev/null
@@ -0,0 +1,467 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.onap.ccsdk.apps.cadi.Access.Level;
+import org.onap.ccsdk.apps.cadi.CachedPrincipal.Resp;
+import org.onap.ccsdk.apps.cadi.principal.CachedBasicPrincipal;
+
+/**
+ * Implement Fast lookup and Cache for Local User Info
+ *
+ * Include ability to add and remove Users
+ *
+ * Also includes a Timer Thread (when necessary) to invoke cleanup on expiring Credentials
+ *
+ * @author Jonathan
+ *
+ */
+public abstract class AbsUserCache<PERM extends Permission> {
+    // Need an obvious key for when there is no Authentication Cred
+    private static final String NO_CRED = "NoCred";
+    static final int MIN_INTERVAL = 1000*60;    // Min 1 min
+    static final int MAX_INTERVAL = 1000*60*60*4; //  4 hour max
+    private static Timer timer;
+    // Map of userName to User
+    private final Map<String, User<PERM>> userMap;
+    private static final Map<String, Miss> missMap = new TreeMap<>();
+    private final Symm missEncrypt;
+
+    private Clean clean;
+    protected Access access;
+
+    protected AbsUserCache(Access access, long cleanInterval, int highCount, int usageCount) {
+        this.access = access;
+        Symm s;
+        try {
+            byte[] gennedKey = Symm.keygen();
+            s = Symm.obtain(new ByteArrayInputStream(gennedKey));
+        } catch (IOException e) {
+            access.log(e);
+            s = Symm.base64noSplit;
+        }
+        missEncrypt = s;
+
+        userMap = new ConcurrentHashMap<>();
+
+
+        if (cleanInterval>0) {
+            cleanInterval = Math.max(MIN_INTERVAL, cleanInterval);
+            synchronized(AbsUserCache.class) { // Lazy instantiate.. in case there is no cleanup needed
+                if (timer==null) {
+                    timer = new Timer("CADI Cleanup Timer",true);
+                }
+
+                timer.schedule(clean = new Clean(access, cleanInterval, highCount, usageCount), cleanInterval, cleanInterval);
+                access.log(Access.Level.INIT, "Cleaning Thread initialized with interval of",cleanInterval, "ms and max objects of", highCount);
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public AbsUserCache(AbsUserCache<PERM> cache) {
+        this.access = cache.access;
+        userMap = cache.userMap;
+        missEncrypt = cache.missEncrypt;
+
+        synchronized(AbsUserCache.class) {
+            if (cache.clean!=null && cache.clean.lur==null && this instanceof CachingLur) {
+                cache.clean.lur=(CachingLur<PERM>)this;
+            }
+        }
+    }
+
+    protected void setLur(CachingLur<PERM> lur) {
+        if (clean!=null)clean.lur = lur;
+
+    }
+
+    protected void addUser(User<PERM> user) {
+        Principal p = user.principal;
+        String key;
+        try {
+            if (p instanceof GetCred) {
+                key = missKey(p.getName(), ((GetCred)p).getCred());
+            } else {
+                byte[] cred;
+                if ((cred=user.getCred())==null) {
+                    key = user.name + NO_CRED;
+                } else {
+                    key = missKey(user.name,cred);
+                }
+            }
+        } catch (IOException e) {
+            access.log(e);
+            return;
+        }
+        userMap.put(key, user);
+    }
+
+    // Useful for looking up by WebToken, etc.
+    protected void addUser(String key, User<PERM> user) {
+        userMap.put(key, user);
+    }
+
+    /**
+     * Add miss to missMap.  If Miss exists, or too many tries, returns false.
+     *
+     * otherwise, returns true to allow another attempt.
+     *
+     * @param key
+     * @param bs
+     * @return
+     * @throws IOException
+     */
+    protected synchronized boolean addMiss(String key, byte[] bs) {
+        String mkey;
+        try {
+            mkey = missKey(key,bs);
+        } catch (IOException e) {
+            access.log(e);
+            return false;
+        }
+        Miss miss = missMap.get(mkey);
+        if (miss==null) {
+            missMap.put(mkey, new Miss(bs,clean==null?MIN_INTERVAL:clean.timeInterval,key));
+            return true;
+        }
+        return miss.mayContinue();
+    }
+
+    protected Miss missed(String key, byte[] bs) throws IOException {
+        return missMap.get(missKey(key,bs));
+    }
+
+    protected User<PERM> getUser(Principal principal) {
+        String key;
+        if (principal instanceof GetCred) {
+            GetCred gc = (GetCred)principal;
+            try {
+                key = missKey(principal.getName(), gc.getCred());
+            } catch (IOException e) {
+                access.log(e, "Error getting key from Principal");
+                key = principal.getName();
+            }
+        } else {
+            key = principal.getName()+NO_CRED;
+        }
+        User<PERM> u = userMap.get(key);
+        if (u!=null) {
+            u.incCount();
+        }
+        return u;
+    }
+
+    protected User<PERM> getUser(CachedBasicPrincipal cbp) {
+        return getUser(cbp.getName(), cbp.getCred());
+    }
+
+    protected User<PERM> getUser(String user, byte[] cred) {
+        User<PERM> u;
+        String key=null;
+        try {
+            key =missKey(user,cred);
+        } catch (IOException e) {
+            access.log(e);
+            return null;
+        }
+        u = userMap.get(key);
+        if (u!=null) {
+            if (u.permExpired()) {
+                userMap.remove(key);
+                u=null;
+            } else {
+                u.incCount();
+            }
+        }
+        return u;
+    }
+
+    /**
+     * Removes User from the Cache
+     * @param user
+     */
+    protected void remove(User<PERM> user) {
+        userMap.remove(user.principal.getName());
+    }
+
+    /**
+     * Removes user from the Cache
+     *
+     * @param user
+     */
+    public void remove(String user) {
+        Object o = userMap.remove(user);
+        if (o!=null) {
+            access.log(Level.INFO, user,"removed from Client Cache by Request");
+        }
+    }
+
+    /**
+     * Clear all Users from the Client Cache
+     */
+    public void clearAll() {
+        userMap.clear();
+    }
+
+    public final List<DumpInfo> dumpInfo() {
+        List<DumpInfo> rv = new ArrayList<>();
+        for (User<PERM> user : userMap.values()) {
+            rv.add(new DumpInfo(user));
+        }
+        return rv;
+    }
+
+    /**
+     * The default behavior of a LUR is to not handle something exclusively.
+     */
+    public boolean handlesExclusively(Permission ... pond) {
+        return false;
+    }
+
+    /**
+     * Container calls when cleaning up...
+     *
+     * If overloading in Derived class, be sure to call "super.destroy()"
+     */
+    public void destroy() {
+        if (timer!=null) {
+            timer.purge();
+            timer.cancel();
+        }
+    }
+
+
+
+    // Simple map of Group name to a set of User Names
+    //    private Map<String, Set<String>> groupMap = new HashMap<>();
+
+    /**
+     * Class to hold a small subset of the data, because we don't want to expose actual Permission or User Objects
+     */
+    public final class DumpInfo {
+        public String user;
+        public List<String> perms;
+
+        public DumpInfo(User<PERM> user) {
+            this.user = user.principal.getName();
+            perms = new ArrayList<>(user.perms.keySet());
+        }
+    }
+
+    /**
+     * Clean will examine resources, and remove those that have expired.
+     *
+     * If "highs" have been exceeded, then we'll expire 10% more the next time.  This will adjust after each run
+     * without checking contents more than once, making a good average "high" in the minimum speed.
+     *
+     * @author Jonathan
+     *
+     */
+    private final class Clean extends TimerTask {
+        private final Access access;
+        private CachingLur<PERM> lur;
+
+        // The idea here is to not be too restrictive on a high, but to Expire more items by
+        // shortening the time to expire.  This is done by judiciously incrementing "advance"
+        // when the "highs" are exceeded.  This effectively reduces numbers of cached items quickly.
+        private final int high;
+        private long advance;
+        private final long timeInterval;
+        private final int usageTriggerCount;
+
+        public Clean(Access access, long cleanInterval, int highCount, int usageTriggerCount) {
+            this.access = access;
+            lur = null;
+            high = highCount;
+            timeInterval = cleanInterval;
+            advance = 0;
+            this.usageTriggerCount=usageTriggerCount;
+        }
+        public void run() {
+            int renewed = 0;
+            int count = 0;
+            int total = 0;
+            try {
+                // look at now.  If we need to expire more by increasing "now" by "advance"
+                ArrayList<User<PERM>> al = new ArrayList<>(userMap.values().size());
+                al.addAll(0, userMap.values());
+                long now = System.currentTimeMillis() + advance;
+                for (User<PERM> user : al) {
+                    ++total;
+                        if (user.count>usageTriggerCount) {
+                            boolean touched = false, removed=false;
+                            if (user.principal instanceof CachedPrincipal) {
+                                CachedPrincipal cp = (CachedPrincipal)user.principal;
+                                if (cp.expires() < now) {
+                                    switch(cp.revalidate(null)) {
+                                        case INACCESSIBLE:
+                                            access.log(Level.AUDIT, "AAF Inaccessible.  Keeping credentials");
+                                            break;
+                                        case REVALIDATED:
+                                            user.resetCount();
+                                            touched = true;
+                                            break;
+                                        default:
+                                            user.resetCount();
+                                            remove(user);
+                                            ++count;
+                                            removed = true;
+                                            break;
+                                    }
+                                }
+                            }
+
+                            if (!removed && lur!=null && user.permExpires<= now ) {
+                                if (lur.reload(user).equals(Resp.REVALIDATED)) {
+                                    user.renewPerm();
+                                    access.log(Level.DEBUG, "Reloaded Perms for",user);
+                                    touched = true;
+                                }
+                            }
+                            user.resetCount();
+                            if (touched) {
+                                ++renewed;
+                            }
+
+                        } else {
+                            if (user.permExpired()) {
+                                remove(user);
+                                ++count;
+                            }
+                        }
+                }
+
+                // Clean out Misses
+                int missTotal = missMap.keySet().size();
+                int miss = 0;
+                if (missTotal>0) {
+                    ArrayList<String> keys = new ArrayList<>(missTotal);
+                    keys.addAll(missMap.keySet());
+                    for (String key : keys) {
+                        Miss m = missMap.get(key);
+                        if (m!=null) {
+                            long timeLeft = m.timestamp - System.currentTimeMillis();
+                            if (timeLeft<0) {
+                                synchronized(missMap) {
+                                    missMap.remove(key);
+                                }
+                                access.log(Level.INFO, m.name, " has been removed from Missed Credential Map (" + m.tries + " invalid tries)");
+                                ++miss;
+                            } else {
+                                access.log(Level.INFO, m.name, " remains in Missed Credential Map (" + m.tries + " invalid tries) for " + (timeLeft/1000) + " more seconds");
+                            }
+                        }
+                    }
+                }
+
+                if (count+renewed+miss>0) {
+                    access.log(Level.INFO, (lur==null?"Cache":lur.getClass().getSimpleName()), "removed",count,
+                        "and renewed",renewed,"expired Permissions out of", total,"and removed", miss, "password misses out of",missTotal);
+                }
+
+                // If High (total) is reached during this period, increase the number of expired services removed for next time.
+                // There's no point doing it again here, as there should have been cleaned items.
+                if (total>high) {
+                    // advance cleanup by 10%, without getting greater than timeInterval.
+                    advance = Math.min(timeInterval, advance+(timeInterval/10));
+                } else {
+                    // reduce advance by 10%, without getting lower than 0.
+                    advance = Math.max(0, advance-(timeInterval/10));
+                }
+            } catch (Exception e) {
+                access.log(Level.ERROR,e.getMessage());
+            }
+        }
+    }
+
+
+    private String missKey(String name, byte[] bs) throws IOException {
+        return name + Hash.toHex(missEncrypt.encode(bs));
+    }
+
+    protected static class Miss {
+        private static final int MAX_TRIES = 3;
+
+        long timestamp;
+
+        private long timetolive;
+
+        private long tries;
+
+        private final String name;
+
+        public Miss(final byte[] first, final long timeInterval, final String name) {
+            timestamp = System.currentTimeMillis() + timeInterval;
+            this.timetolive = timeInterval;
+            tries = 0L;
+            this.name = name;
+        }
+
+
+        public synchronized boolean mayContinue() {
+            long ts = System.currentTimeMillis();
+            if (ts>timestamp) {
+                tries = 0;
+                timestamp = ts + timetolive;
+            } else if (MAX_TRIES <= ++tries) {
+                return false;
+            }
+            return true;
+        }
+
+    }
+
+    /**
+     * Report on state
+     */
+    public String toString() {
+        return getClass().getSimpleName() +
+                " Cache:\n  Users Cached: " +
+                userMap.size() +
+                "\n  Misses Saved: " +
+                missMap.size() +
+                '\n';
+
+    }
+
+    public void clear(Principal p, StringBuilder sb) {
+        sb.append(toString());
+        userMap.clear();
+        missMap.clear();
+        access.log(Level.AUDIT, p.getName(),"has cleared User Cache in",getClass().getSimpleName());
+        sb.append("Now cleared\n");
+    }
+
+}
\ No newline at end of file
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Access.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Access.java
new file mode 100644 (file)
index 0000000..1d28405
--- /dev/null
@@ -0,0 +1,180 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+/**
+ * Various Environments require different logging mechanisms, or at least allow
+ * for different ones. We need the Framework to be able to hook into any particular instance of logging
+ * mechanism, whether it be a Logging Object within a Servlet Context, or a direct library like log4j.
+ * This interface, therefore, allows maximum pluggability in a variety of different app styles.
+ *
+ *
+ *
+ */
+public interface Access {
+    // levels to use
+    public enum Level {
+        DEBUG(0x1), INFO(0x10), AUDIT(0x100), WARN(0x2000), ERROR(0x4000), INIT(0x8000),TRACE(0x10000),NONE(0XFFFF);
+        private final int bit;
+
+        Level(int ord) {
+            bit = ord;
+        }
+
+        public boolean inMask(int mask) {
+            return (mask & bit) == bit;
+        }
+
+        public int addToMask(int mask) {
+            return mask | bit;
+        }
+
+        public int delFromMask(int mask) {
+            return mask & ~bit;
+        }
+
+        public int toggle(int mask) {
+            if (inMask(mask)) {
+                return delFromMask(mask);
+            } else {
+                return addToMask(mask);
+            }
+        }
+
+
+        public int maskOf() {
+            int mask=0;
+            for (Level l : values()) {
+                if (ordinal()<=l.ordinal() && l!=NONE) {
+                    mask|=l.bit;
+                }
+            }
+            return mask;
+        }
+    }
+
+    /**
+     * Write a variable list of Object's text via the toString() method with appropriate space, etc.
+     * @param elements
+     */
+    public void log(Level level, Object ... elements);
+
+    /**
+     * Printf mechanism for Access
+     * @param level
+     * @param fmt
+     * @param elements
+     */
+    public void printf(Level level, String fmt, Object ... elements);
+
+    /**
+     * Check if message will log before constructing
+     * @param level
+     * @return
+     */
+    public boolean willLog(Level level);
+
+    /**
+     * Write the contents of an exception, followed by a variable list of Object's text via the
+     * toString() method with appropriate space, etc.
+     *
+     * The Loglevel is always "ERROR"
+     *
+     * @param elements
+     */
+    public void log(Exception e, Object ... elements);
+
+    /**
+     * Set the Level to compare logging too
+     */
+    public void setLogLevel(Level level);
+
+    /**
+     * It is important in some cases to create a class from within the same Classloader that created
+     * Security Objects.  Specifically, it's pretty typical for Web Containers to separate classloaders
+     * so as to allow Apps with different dependencies.
+     * @return
+     */
+    public ClassLoader classLoader();
+
+    public String getProperty(String string, String def);
+
+    public Properties getProperties();
+
+    public void load(InputStream is) throws IOException;
+
+    /**
+     * if "anytext" is true, then decryption will always be attempted.  Otherwise, only if starts with
+     * Symm.ENC
+     * @param encrypted
+     * @param anytext
+     * @return
+     * @throws IOException
+     */
+    public String decrypt(String encrypted, boolean anytext) throws IOException;
+
+    public static final Access NULL = new Access() {
+        public void log(Level level, Object... elements) {
+        }
+
+        @Override
+        public void printf(Level level, String fmt, Object... elements) {
+        }
+
+        public void log(Exception e, Object... elements) {
+        }
+
+        public ClassLoader classLoader() {
+            return ClassLoader.getSystemClassLoader();
+        }
+
+        public String getProperty(String string, String def) {
+            return null;
+        }
+
+        public void load(InputStream is) throws IOException {
+        }
+
+        public void setLogLevel(Level level) {
+        }
+
+        public String decrypt(String encrypted, boolean anytext) throws IOException {
+            return encrypted;
+        }
+
+        @Override
+        public boolean willLog(Level level) {
+            return false;
+        }
+
+        @Override
+        public Properties getProperties() {
+            return new Properties();
+        }
+    };
+
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/BasicCred.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/BasicCred.java
new file mode 100644 (file)
index 0000000..37c5a2e
--- /dev/null
@@ -0,0 +1,35 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+/**
+ * An Interface for testing on Requests to see if we can get a User and Password
+ * It works for CadiWrap, but also, Container Specific Wraps (aka Tomcat) should also
+ * implement.
+ *
+ *
+ */
+public interface BasicCred extends GetCred {
+    public void setUser(String user);
+    public void setCred(byte[] passwd);
+    public String getUser();
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/BufferedServletInputStream.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/BufferedServletInputStream.java
new file mode 100644 (file)
index 0000000..86ae190
--- /dev/null
@@ -0,0 +1,223 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import jakarta.servlet.ReadListener;
+import jakarta.servlet.ServletInputStream;
+
+/**
+ * BufferedServletInputStream
+ *
+ * There are cases in brain-dead middleware (SOAP) where they store routing information in the content.
+ *
+ * In HTTP, this requires reading the content from the InputStream which, of course, cannot be re-read.
+ *
+ * BufferedInputStream exists to implement the "Mark" protocols for Streaming, which will enable being
+ * re-read.  Unfortunately, J2EE chose to require a "ServletInputStream" as an abstract class, rather than
+ * an interface, which requires we create a delegating pattern, rather than the preferred inheriting pattern.
+ *
+ * Unfortunately, the standard "BufferedInputStream" cannot be used, because it simply creates a byte array
+ * in the "mark(int)" method of that size.  This is not appropriate for this application, because the Header
+ * can be potentially huge, and if a buffer was allocated to accommodate all possibilities, the cost of memory
+ * allocation would be too large for high performance transactions.
+ *
+ *
+ *
+ */
+public class BufferedServletInputStream extends ServletInputStream {
+    private static final int NONE = 0;
+    private static final int STORE = 1;
+    private static final int READ = 2;
+
+    private InputStream is;
+    private int state = NONE;
+    private Capacitor capacitor;
+
+    public BufferedServletInputStream(InputStream is) {
+        this.is = is;
+        capacitor = null;
+    }
+
+
+    public int read() throws IOException {
+        int value=-1;
+        if (capacitor==null) {
+            value=is.read();
+        } else {
+            switch(state) {
+                case STORE:
+                    value = is.read();
+                    if (value>=0) {
+                        capacitor.put((byte)value);
+                    }
+                    break;
+                case READ:
+                    value = capacitor.read();
+                    if (value<0) {
+                        capacitor.done();
+                        capacitor=null; // all done with buffer
+                        value = is.read();
+                    }
+            }
+        }
+        return value;
+    }
+    @Override
+    public int read(byte[] b) throws IOException {
+        return read(b,0,b.length);
+    }
+    
+  @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        int count = -1;
+        if (capacitor==null) {
+            count = is.read(b,off,len);
+        } else {
+            switch(state) {
+                case STORE:
+                    count = is.read(b, off, len);
+                    if (count>0) {
+                        capacitor.put(b, off, count);
+                    }
+                    break;
+                case READ:
+                    count = capacitor.read(b, off, len);
+                    if (count<=0) {
+                        capacitor.done();
+                        capacitor=null; // all done with buffer
+                    }
+                    if (count<len) {
+                        int temp = is.read(b, count, len-count);
+                        if (temp>0) { // watch for -1
+                            count+=temp;
+                        } else if (count<=0) {
+                            count = temp; // must account for Stream coming back -1
+                        }
+                    }
+                    break;
+            }
+        }
+        return count;
+    }
+
+    public long skip(long n) throws IOException {
+        long skipped = capacitor.skip(n);
+        if (skipped<n) {
+            skipped += is.skip(n-skipped);
+        }
+        return skipped;
+    }
+
+
+    public int available() throws IOException {
+        int count = is.available();
+        if (capacitor!=null)count+=capacitor.available();
+        return count;
+    }
+
+    /**
+     * Return just amount buffered (for debugging purposes, mostly)
+     * @return
+     */
+    public int buffered() {
+        return capacitor.available();
+    }
+
+
+    public void close() throws IOException {
+        if (capacitor!=null) {
+            capacitor.done();
+            capacitor=null;
+        }
+        is.close();
+    }
+
+
+    /**
+     * Note: Readlimit is ignored in this implementation, because the need was for unknown buffer size which wouldn't
+     * require allocating and dumping huge chunks of memory every use, or risk overflow.
+     */
+    public synchronized void mark(int readlimit) {
+        switch(state) {
+            case NONE:
+                capacitor = new Capacitor();
+                break;
+            case READ:
+                capacitor.done();
+                break;
+        }
+        state = STORE;
+    }
+
+
+    /**
+     * Reset Stream
+     *
+     * Calling this twice is not supported in typical Stream situations, but it is allowed in this service.  The caveat is that it can only reset
+     * the data read in since Mark has been called.  The data integrity is only valid if you have not continued to read past what is stored.
+     *
+     */
+    public synchronized void reset() throws IOException {
+        switch(state) {
+            case STORE:
+                capacitor.setForRead();
+                state = READ;
+                break;
+            case READ:
+                capacitor.reset();
+                break;
+            case NONE:
+                throw new IOException("InputStream has not been marked");
+        }
+    }
+
+
+    public boolean markSupported() {
+        return true;
+    }
+
+
+    @Override
+    public boolean isFinished() {
+        
+        try {
+            return (this.is.available() == 0);
+        } catch (IOException e) {
+            return true;
+        }
+    }
+
+
+    @Override
+    public boolean isReady() {
+        return true;
+    }
+
+
+    @Override
+    public void setReadListener(ReadListener arg0) {
+        throw new UnsupportedOperationException("Unimplemented method 'setReadListener'");
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/CachedPrincipal.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/CachedPrincipal.java
new file mode 100644 (file)
index 0000000..6af19e6
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+import java.security.Principal;
+
+/**
+ * Cached Principals need to be able to revalidate in the background.
+ *
+ *
+ */
+public interface CachedPrincipal extends Principal {
+    public enum Resp {NOT_MINE,UNVALIDATED,REVALIDATED,INACCESSIBLE,DENIED};
+
+    /**
+     * Re-validate with Creator
+     *
+     * @return
+     */
+    public abstract Resp revalidate(Object state);
+
+    /**
+     * Store when last updated.
+     * @return
+     */
+    public abstract long expires();
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/CachingLur.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/CachingLur.java
new file mode 100644 (file)
index 0000000..5b3ac9d
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+import java.security.Principal;
+
+import org.onap.ccsdk.apps.cadi.CachedPrincipal.Resp;
+
+
+public interface CachingLur<PERM extends Permission> extends Lur {
+    public abstract void remove(String user);
+    public abstract Resp reload(User<PERM> user);
+    public abstract void setDebug(String commaDelimIDsOrNull);
+    public abstract void clear(Principal p, StringBuilder sb);
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/CadiException.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/CadiException.java
new file mode 100644 (file)
index 0000000..c0dfce9
--- /dev/null
@@ -0,0 +1,49 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+/**
+ * CADI Specific Exception
+ */
+public class CadiException extends Exception {
+    /**
+     *  Generated ID
+     */
+    private static final long serialVersionUID = -4180145363107742619L;
+
+    public CadiException() {
+        super();
+    }
+
+    public CadiException(String message) {
+        super(message);
+    }
+
+    public CadiException(Throwable cause) {
+        super(cause);
+    }
+
+    public CadiException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/CadiWrap.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/CadiWrap.java
new file mode 100644 (file)
index 0000000..495076b
--- /dev/null
@@ -0,0 +1,200 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequestWrapper;
+
+import org.onap.ccsdk.apps.cadi.Access.Level;
+import org.onap.ccsdk.apps.cadi.filter.NullPermConverter;
+import org.onap.ccsdk.apps.cadi.filter.PermConverter;
+import org.onap.ccsdk.apps.cadi.lur.EpiLur;
+import org.onap.ccsdk.apps.cadi.principal.TaggedPrincipal;
+import org.onap.ccsdk.apps.cadi.taf.TafResp;
+import org.onap.ccsdk.apps.cadi.util.Timing;
+
+
+
+/**
+ * Inherit the HttpServletRequestWrapper, which calls methods of delegate it's created with, but
+ * overload the key security mechanisms with CADI mechanisms
+ *
+ * This works with mechanisms working strictly with HttpServletRequest (i.e. Servlet Filters)
+ *
+ * Specialty cases, i.e. Tomcat, which for their containers utilize their own mechanisms and Wrappers, you may
+ * need something similar.  See AppServer specific code (i.e. tomcat) for these.
+ *
+ * @author Jonathan
+ *
+ */
+public class CadiWrap extends HttpServletRequestWrapper implements HttpServletRequest, BasicCred {
+    private TaggedPrincipal principal;
+    private Lur lur;
+    private String user; // used to set user/pass from brain-dead protocols like WSSE
+    private byte[] password;
+    private PermConverter pconv;
+    private Access access;
+
+    /**
+     * Standard Wrapper constructor for Delegate pattern
+     * @param request
+     */
+    public CadiWrap(HttpServletRequest request, TafResp tafResp, Lur lur) {
+        super(request);
+        principal = tafResp.getPrincipal();
+        access = tafResp.getAccess();
+        this.lur = lur;
+        pconv = NullPermConverter.singleton();
+    }
+
+    /**
+     * Standard Wrapper constructor for Delegate pattern, with PermConverter
+     * @param request
+     */
+    public CadiWrap(HttpServletRequest request, TafResp tafResp, Lur lur, PermConverter pc) {
+        super(request);
+        principal = tafResp.getPrincipal();
+        access = tafResp.getAccess();
+        this.lur = lur;
+        pconv = pc;
+    }
+
+
+    /**
+     * Part of the HTTP Security API.  Declare the User associated with this HTTP Transaction.
+     * CADI does this by reporting the name associated with the Principal obtained, if any.
+     */
+    @Override
+    public String getRemoteUser() {
+        return principal==null?null:principal.getName();
+    }
+
+    /**
+     * Part of the HTTP Security API.  Return the User Principal associated with this HTTP
+     * Transaction.
+     */
+    @Override
+    public Principal getUserPrincipal() {
+        return principal;
+    }
+
+    /**
+     * This is the key API call for AUTHZ in J2EE.  Given a Role (String passed in), is the user
+     * associated with this HTTP Transaction allowed to function in this Role?
+     *
+     * For CADI, we pass the responsibility for determining this to the "LUR", which may be
+     * determined by the Enterprise.
+     *
+     * Note: Role check is also done in "CadiRealm" in certain cases...
+     *
+     *
+     */
+    @Override
+    public boolean isUserInRole(String perm) {
+        return perm==null?false:checkPerm(access,"isUserInRole",principal,pconv,lur,perm);
+    }
+
+    public static boolean checkPerm(Access access, String caller, Principal principal, PermConverter pconv, Lur lur, String perm) {
+        if (principal== null) {
+            access.log(Level.AUDIT,caller, "No Principal in Transaction");
+            return false;
+        } else {
+            final long start = System.nanoTime();
+            perm = pconv.convert(perm);
+            if (lur.fish(principal,lur.createPerm(perm))) {
+                access.printf(Level.DEBUG,"%s: %s has %s, %f ms", caller, principal.getName(), perm, Timing.millis(start));
+                return true;
+            } else {
+                access.printf(Level.DEBUG,"%s: %s does not have %s, %f ms", caller, principal.getName(), perm, Timing.millis(start));
+                return false;
+            }
+        }
+
+    }
+
+    /**
+     * CADI Function (Non J2EE standard). GetPermissions will read the Permissions from AAF (if configured) and Roles from Local Lur, etc
+     *  as implemented with lur.fishAll
+     *
+     *  To utilize, the Request must be a "CadiWrap" object, then call.
+     */
+    public List<Permission> getPermissions(Principal p) {
+        List<Permission> perms = new ArrayList<>();
+        lur.fishAll(p, perms);
+        return perms;
+    }
+    /**
+     * Allow setting of tafResp and lur after construction
+     *
+     * This can happen if the CadiWrap is constructed in a Valve other than CadiValve
+     */
+    public void set(TafResp tafResp, Lur lur) {
+        principal = tafResp.getPrincipal();
+        access = tafResp.getAccess();
+        this.lur = lur;
+    }
+
+    public String getUser() {
+        if (user==null && principal!=null) {
+            user = principal.getName();
+        }
+        return user;
+    }
+
+    public byte[] getCred() {
+        return password;
+    }
+
+    public void setUser(String user) {
+        this.user = user;
+    }
+
+    public void setCred(byte[] passwd) {
+        password = passwd;
+    }
+
+    public CadiWrap setPermConverter(PermConverter pc) {
+        pconv = pc;
+        return this;
+    }
+
+    // Add a feature
+    public void invalidate(String id) {
+        if (lur instanceof EpiLur) {
+            ((EpiLur)lur).remove(id);
+        } else if (lur instanceof CachingLur) {
+            ((CachingLur<?>)lur).remove(id);
+        }
+    }
+
+    public Lur getLur() {
+        return lur;
+    }
+
+    public Access access() {
+        return access;
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Capacitor.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Capacitor.java
new file mode 100644 (file)
index 0000000..2d8a722
--- /dev/null
@@ -0,0 +1,241 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * Capacitor
+ *
+ * Storage mechanism for read data, specifically designed for InputStreams.
+ *
+ * The Standard BufferedInputStream requires a limit to be set for buffered reading, which is
+ * impractical for reading SOAP headers, which can be quite large.
+ * @author Jonathan
+ *
+ */
+public class Capacitor {
+    private static final int DEFAULT_CHUNK = 256;
+    private ArrayList<ByteBuffer> bbs = new ArrayList<>();
+    private ByteBuffer curr = null;
+    private int idx;
+
+    // Maintain a private RingBuffer for Memory, for efficiency
+    private static ByteBuffer[] ring = new ByteBuffer[16];
+    private static int start, end;
+
+
+    public void put(byte b) {
+        if (curr == null || curr.remaining()==0) { // ensure we have a "curr" buffer ready for data
+            curr = ringGet();
+            bbs.add(curr);
+        }
+        curr.put(b);
+    }
+
+    public int read() {
+        if (curr!=null) {
+            if (curr.remaining()>0) { // have a buffer, use it!
+                return curr.get();
+            } else if (idx<bbs.size()){ // Buffer not enough, get next one from array
+                curr=bbs.get(idx++);
+                return curr.get();
+            }
+        } // if no curr buffer, treat as end of stream
+        return -1;
+    }
+
+    /**
+     * read into an array like Streams
+     *
+     * @param array
+     * @param offset
+     * @param length
+     * @return
+     */
+    public int read(byte[] array, int offset, int length) {
+        if (curr==null)return -1;
+        int len;
+        int count=0;
+        while (length>0) { // loop through while there's data needed
+            if ((len=curr.remaining())>length) { //  if enough data in curr buffer, use this code
+                curr.get(array,offset,length);
+                count+=length;
+                length=0;
+            } else {  // get data from curr, mark how much is needed to fulfil, and loop for next curr.
+                curr.get(array,offset,len);
+                count+=len;
+                offset+=len;
+                length-=len;
+                if (idx<bbs.size()) {
+                    curr=bbs.get(idx++);
+                } else {
+                    length=0; // stop, and return the count of how many we were able to load
+                }
+            }
+        }
+        return count;
+    }
+
+    /**
+     * Put an array of data into Capacitor
+     *
+     * @param array
+     * @param offset
+     * @param length
+     */
+    public void put(byte[] array, int offset, int length) {
+        if (curr == null || curr.remaining()==0) {
+            curr = ringGet();
+            bbs.add(curr);
+        }
+
+        int len;
+        while (length>0) {
+            if ((len=curr.remaining())>length) {
+                curr.put(array,offset,length);
+                length=0;
+            } else {
+//                System.out.println(new String(array));
+                curr.put(array,offset,len);
+                length-=len;
+                offset+=len;
+                curr = ringGet();
+                bbs.add(curr);
+            }
+        }
+    }
+
+    /**
+     * Move state from Storage mode into Read mode, changing all internal buffers to read mode, etc
+     */
+    public void setForRead() {
+        for (ByteBuffer bb : bbs) {
+            bb.flip();
+        }
+        if (bbs.isEmpty()) {
+            curr = null;
+            idx = 0;
+        } else {
+            curr=bbs.get(0);
+            idx=1;
+        }
+    }
+
+    /**
+     * reuse all the buffers
+     */
+    public void done() {
+        for (ByteBuffer bb : bbs) {
+            ringPut(bb);
+        }
+        bbs.clear();
+        curr = null;
+    }
+
+    /**
+     * Declare amount of data available to be read at once.
+     *
+     * @return
+     */
+    public int available() {
+        int count = 0;
+        for (ByteBuffer bb : bbs) {
+            count+=bb.remaining();
+        }
+        return count;
+    }
+
+    /**
+     * Returns how many are left that were not skipped
+     * @param n
+     * @return
+     */
+    public long skip(long n) {
+        long skipped=0L;
+        int skip;
+        if (curr==null) {
+            return 0;
+        }
+        while (n>0) {
+            if (n<(skip=curr.remaining())) {
+                curr.position(curr.position()+(int)n);
+                skipped+=skip;
+                n=0;
+            } else {
+                curr.position(curr.limit());
+
+                skipped-=skip;
+                if (idx<bbs.size()) {
+                    curr=bbs.get(idx++);
+                    n-=skip;
+                } else {
+                    n=0;
+                }
+            }
+        }
+        return skipped > 0 ? skipped : 0;
+    }
+    /**
+     * Be able to re-read data that is stored that has already been re-read.  This is not a standard Stream behavior, but can be useful
+     * in a standalone mode.
+     */
+    public void reset() {
+        for (ByteBuffer bb : bbs) {
+            bb.position(0);
+        }
+        if (bbs.isEmpty()) {
+            curr = null;
+            idx = 0;
+        } else {
+            curr=bbs.get(0);
+            idx=1;
+        }
+    }
+
+    /*
+     * Ring Functions.  Reuse allocated memory
+     */
+    private ByteBuffer ringGet() {
+        ByteBuffer bb = null;
+        synchronized(ring) {
+            bb=ring[start];
+            ring[start]=null;
+            if (bb!=null && ++start>15)start=0;
+        }
+        if (bb==null) {
+            bb=ByteBuffer.allocate(DEFAULT_CHUNK);
+        } else {
+            bb.clear();// refresh reused buffer
+        }
+        return bb;
+    }
+
+    private void ringPut(ByteBuffer bb) {
+        synchronized(ring) {
+            ring[end]=bb; // if null or not, BB will just be Garbage collected
+            if (++end>15)end=0;
+        }
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/CmdLine.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/CmdLine.java
new file mode 100644 (file)
index 0000000..f4ac0f4
--- /dev/null
@@ -0,0 +1,357 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.security.NoSuchAlgorithmException;
+
+import org.onap.ccsdk.apps.cadi.util.Chmod;
+import org.onap.ccsdk.apps.cadi.util.JsonOutputStream;
+
+
+
+/**
+ * A Class to run on command line to determine suitability of environment for certain TAFs.
+ *  *
+ * @author Jonathan
+ *
+ */
+public class CmdLine {
+
+    private static boolean systemExit = true;
+    /**
+     * @param args
+     */
+    public static void main(String[] args) {
+        if (args.length>0) {
+            if ("digest".equalsIgnoreCase(args[0]) && (args.length>2 || (args.length>1 && System.console()!=null))) {
+                String keyfile;
+                String password;
+                if (args.length>2) {
+                    password = args[1];
+                    keyfile = args[2];
+                    if ("-i".equals(password)) {
+                        int c;
+                        StringBuilder sb = new StringBuilder();
+                        try {
+                            while ((c=System.in.read())>=0) {
+                                sb.append((char)c);
+                            }
+                        } catch (IOException e) {
+                            e.printStackTrace();
+                        }
+                        password = sb.toString();
+                    }
+                } else {
+                    keyfile = args[1];
+                    password = new String(System.console().readPassword("Type here (keystrokes hidden): "));
+                }
+
+                try {
+                    Symm symm;
+                    FileInputStream fis = new FileInputStream(keyfile);
+                    try {
+                        symm = Symm.obtain(fis);
+                    } finally {
+                        fis.close();
+                    }
+                    symm.enpass(password, System.out);
+                    System.out.println();
+                    System.out.flush();
+                    return;
+                    /*  testing code... don't want it exposed
+                    System.out.println(" ******** Testing *********");
+                    for (int i=0;i<100000;++i) {
+                        System.out.println(args[1]);
+                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                        b64.enpass(args[1], baos);
+                        String pass;
+                        System.out.println(pass=new String(baos.toByteArray()));
+                        ByteArrayOutputStream reconstituted = new ByteArrayOutputStream();
+                        b64.depass(pass, reconstituted);
+                        String r = reconstituted.toString();
+                        System.out.println(r);
+                        if (!r.equals(args[1])) {
+                            System.err.println("!!!!! STOP - ERROR !!!!!");
+                            return;
+                        }
+                        System.out.println();
+                    }
+                    System.out.flush();
+                    */
+
+                } catch (IOException e) {
+                    System.err.println("Cannot digest password");
+                    System.err.println("   \""+ e.getMessage() + '"');
+                }
+// DO NOT LEAVE THIS METHOD Compiled IN CODE... Do not want looking at passwords on disk too easy
+// Jonathan.  Oh, well, Deployment services need this behavior.  I will put this code in, but leave it undocumented.
+// One still needs access to the keyfile to read.
+// July 2016 - thought of a tool "CMPass" to regurgitate from properties, but only if allowed.
+            } else if (("regurgitate".equalsIgnoreCase(args[0]) || "undigest".equalsIgnoreCase(args[0]))
+                        && args.length>2) {
+                try {
+                    Symm symm;
+                    FileInputStream fis = new FileInputStream(args[2]);
+                    try {
+                        symm = Symm.obtain(fis);
+                    } finally {
+                        fis.close();
+                    }
+                    boolean isFile = false;
+                    if ("-i".equals(args[1]) || (isFile="-f".equals(args[1]))) {
+                        BufferedReader br;
+                        if (isFile) {
+                            if (args.length<4) {
+                                System.err.println("Filename in 4th position");
+                                return;
+                            }
+                            br = new BufferedReader(new FileReader(args[3]));
+                        } else {
+                            br = new BufferedReader(new InputStreamReader(System.in));
+                        }
+                        try {
+                            String line;
+                            boolean cont = false;
+                            StringBuffer sb = new StringBuffer();
+                            JsonOutputStream jw = new JsonOutputStream(System.out);
+                            while ((line=br.readLine())!=null) {
+                                if (cont) {
+                                    int end;
+                                    if ((end=line.indexOf('"'))>=0) {
+                                        sb.append(line,0,end);
+                                        cont=false;
+                                    } else {
+                                        sb.append(line);
+                                    }
+                                } else {
+                                    int idx;
+                                    if ((idx = line.indexOf(' '))>=0
+                                            && (idx = line.indexOf(' ',++idx))>0
+                                            && (idx = line.indexOf('=',++idx))>0
+                                            ) {
+                                        System.out.println(line.substring(0, idx-5));
+                                        int start = idx+2;
+                                        int end;
+                                        if ((end=line.indexOf('"',start))<0) {
+                                            end = line.length();
+                                            cont = true;
+                                        }
+                                        sb.append(line,start,end);
+                                    }
+                                }
+                                if (sb.length()>0) {
+                                    symm.depass(sb.toString(),jw);
+                                    if (!cont) {
+                                        System.out.println();
+                                    }
+                                }
+                                System.out.flush();
+                                sb.setLength(0);
+                                if (!cont) {
+                                    jw.resetIndent();
+                                }
+                            }
+                        } finally {
+                            if (isFile) {
+                                br.close();
+                            }
+                        }
+                    } else {
+                        symm.depass(args[1], System.out);
+                    }
+                    System.out.println();
+                    System.out.flush();
+                    return;
+                } catch (IOException e) {
+                    System.err.println("Cannot undigest password");
+                    System.err.println("   \""+ e.getMessage() + '"');
+                }
+            } else if ("encode64".equalsIgnoreCase(args[0]) && args.length>1) {
+                try {
+                    Symm.base64.encode(args[1], System.out);
+                    System.out.println();
+                    System.out.flush();
+                    return;
+                } catch (IOException e) {
+                    System.err.println("Cannot encode Base64 with " + args[1]);
+                    System.err.println("   \""+ e.getMessage() + '"');
+                }
+            } else if ("decode64".equalsIgnoreCase(args[0]) && args.length>1) {
+                try {
+                    Symm.base64.decode(args[1], System.out);
+                    System.out.println();
+                    System.out.flush();
+                    return;
+                } catch (IOException e) {
+                    System.err.println("Cannot decode Base64 text from " + args[1]);
+                    System.err.println("   \""+ e.getMessage() + '"');
+                }
+            } else if ("encode64url".equalsIgnoreCase(args[0]) && args.length>1) {
+                try {
+                    Symm.base64url.encode(args[1], System.out);
+                    System.out.println();
+                    System.out.flush();
+                    return;
+                } catch (IOException e) {
+                    System.err.println("Cannot encode Base64url with " + args[1]);
+                    System.err.println("   \""+ e.getMessage() + '"');
+                }
+            } else if ("decode64url".equalsIgnoreCase(args[0]) && args.length>1) {
+                try {
+                    Symm.base64url.decode(args[1], System.out);
+                    System.out.println();
+                    System.out.flush();
+                    return;
+                } catch (IOException e) {
+                    System.err.println("Cannot decode Base64url text from " + args[1]);
+                    System.err.println("   \""+ e.getMessage() + '"');
+                }
+            } else if ("md5".equalsIgnoreCase(args[0]) && args.length>1) {
+                try {
+                    System.out.println(Hash.hashMD5asStringHex(args[1]));
+                    System.out.flush();
+                } catch (NoSuchAlgorithmException e) {
+                    System.err.println("Cannot hash MD5 from " + args[1]);
+                    System.err.println("   \""+ e.getMessage() + '"');
+                }
+                return;
+            } else if ("sha256".equalsIgnoreCase(args[0]) && args.length>1) {
+                try {
+                    if (args.length>2) {
+                        int max = args.length>7?7:args.length;
+                        for (int i=2;i<max;++i) {
+                            int salt = Integer.parseInt(args[i]);
+                            System.out.println(Hash.hashSHA256asStringHex(args[1],salt));
+                        }
+                    } else {
+                        System.out.println(Hash.hashSHA256asStringHex(args[1]));
+                    }
+                } catch (NoSuchAlgorithmException e) {
+                    System.err.println("Cannot hash SHA256 text from " + args[1]);
+                    System.err.println("   \""+ e.getMessage() + '"');
+                }
+                System.out.flush();
+                return;
+            } else if ("keygen".equalsIgnoreCase(args[0])) {
+                try {
+                    if (args.length>1) {
+                        File f = new File(args[1]);
+                        FileOutputStream fos = new FileOutputStream(f);
+                        try {
+                            fos.write(Symm.keygen());
+                            fos.flush();
+                        } finally {
+                            fos.close();
+                            Chmod.to400.chmod(f);
+                        }
+                    } else {
+                        // create a Symmetric Key out of same characters found in base64
+                        System.out.write(Symm.keygen());
+                        System.out.flush();
+                    }
+                    return;
+                } catch (IOException e) {
+                    System.err.println("Cannot create a key " + args[0]);
+                    System.err.println("   \""+ e.getMessage() + '"');
+                }
+
+            } else if ("passgen".equalsIgnoreCase(args[0])) {
+                int numDigits;
+                if (args.length <= 1) {
+                    numDigits = 24;
+                } else {
+                    numDigits = Integer.parseInt(args[1]);
+                    if (numDigits<8)numDigits = 8;
+                }
+                String pass;
+                boolean noLower,noUpper,noDigits,noSpecial,repeatingChars,missingChars;
+                do {
+                    pass = Symm.randomGen(numDigits);
+                    missingChars=noLower=noUpper=noDigits=noSpecial=true;
+                    repeatingChars=false;
+                    int c=-1,last;
+                    for (int i=0;i<numDigits;++i) {
+                        last = c;
+                        c = pass.charAt(i);
+                        if (c==last) {
+                            repeatingChars=true;
+                            break;
+                        }
+                        if (noLower) {
+                            noLower=!(c>=0x61 && c<=0x7A);
+                        }
+                        if (noUpper) {
+                            noUpper=!(c>=0x41 && c<=0x5A);
+                        }
+                        if (noDigits) {
+                            noDigits=!(c>=0x30 && c<=0x39);
+                        }
+                        if (noSpecial) {
+                            noSpecial = "+!@#$%^&*(){}[]?:;,.".indexOf(c)<0;
+                        }
+
+                        missingChars = (noLower || noUpper || noDigits || noSpecial);
+                    }
+                } while (missingChars || repeatingChars);
+                System.out.println(pass.substring(0,numDigits));
+            } else if ("urlgen".equalsIgnoreCase(args[0])) {
+                int numDigits;
+                if (args.length <= 1) {
+                    numDigits = 24;
+                } else {
+                    numDigits = Integer.parseInt(args[1]);
+                }
+                System.out.println(Symm.randomGen(Symm.base64url.codeset, numDigits).substring(0,numDigits));
+            }
+        } else {
+            System.out.println("Usage: java -jar <this jar> ...");
+            System.out.println("  keygen [<keyfile>]                     (Generates Key on file, or Std Out)");
+            System.out.println("  digest [<passwd>|-i|] <keyfile>        (Encrypts Password with \"keyfile\"");
+            System.out.println("                                          if passwd = -i, will read StdIn");
+            System.out.println("                                          if passwd is blank, will ask securely)");
+            System.out.println("  undigest <enc:...> <keyfile>           (Decrypts Encoded with \"keyfile\")");
+            System.out.println("  passgen <digits>                       (Generate Password of given size)");
+            System.out.println("  urlgen <digits>                        (Generate URL field of given size)");
+            System.out.println("  encode64 <your text>                   (Encodes to Base64)");
+            System.out.println("  decode64 <base64 encoded text>         (Decodes from Base64)");
+            System.out.println("  encode64url <your text>                (Encodes to Base64 URL charset)");
+            System.out.println("  decode64url <base64url encoded text>   (Decodes from Base64 URL charset)");
+            System.out.println("  sha256 <text> <salts(s)>               (Digest String into SHA256 Hash)");
+            System.out.println("  md5 <text>                             (Digest String into MD5 Hash)");
+        }
+        if (systemExit) {
+            System.exit(1);
+        }
+    }
+
+    public static void setSystemExit(boolean shouldExit) {
+        systemExit = shouldExit;
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Connector.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Connector.java
new file mode 100644 (file)
index 0000000..04e3b1d
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+@FunctionalInterface
+public interface Connector {
+    public Lur newLur() throws CadiException;
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/CredVal.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/CredVal.java
new file mode 100644 (file)
index 0000000..ea2b7a7
--- /dev/null
@@ -0,0 +1,41 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+
+/**
+ * UserPass
+ *
+ * The essential interface required by BasicAuth to determine if a given User/Password combination is
+ * valid.  This is done as an interface.
+ *
+ */
+public interface CredVal {
+    public enum Type{PASSWORD};
+    /**
+     *  Validate if the User/Password combination matches records
+     * @param user
+     * @param pass
+     * @return
+     */
+    public boolean validate(String user, Type type, byte[] cred, Object state);
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/CredValDomain.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/CredValDomain.java
new file mode 100644 (file)
index 0000000..727ac8e
--- /dev/null
@@ -0,0 +1,25 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+package org.onap.ccsdk.apps.cadi;
+
+public interface CredValDomain extends CredVal {
+    public String domain();
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/GetCred.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/GetCred.java
new file mode 100644 (file)
index 0000000..647b89e
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+
+@FunctionalInterface
+public interface GetCred {
+    byte[] getCred();
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Hash.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Hash.java
new file mode 100644 (file)
index 0000000..09cf2de
--- /dev/null
@@ -0,0 +1,266 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+import java.nio.ByteBuffer;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * 
+ * 
+ *
+ */
+public class Hash {
+    private static char hexDigit[] = "0123456789abcdef".toCharArray();
+
+/////////////////////////////////
+// MD5
+/////////////////////////////////
+    /**
+     * Encrypt MD5 from Byte Array to Byte Array
+     * @param input
+     * @return
+     * @throws NoSuchAlgorithmException
+     */
+    public static byte[] hashMD5 (byte[] input) throws NoSuchAlgorithmException {
+        // Note: Protect against Multi-thread issues with new MessageDigest
+        MessageDigest md = MessageDigest.getInstance("MD5");
+        md.update(input);
+        return md.digest();
+    }
+
+    /**
+     * Encrypt MD5 from Byte Array to Byte Array
+     * @param input
+     * @return
+     * @throws NoSuchAlgorithmException
+     */
+    public static byte[] hashMD5 (byte[] input, int offset, int length) throws NoSuchAlgorithmException {
+        // Note: Protect against Multi-thread issues with new MessageDigest
+        MessageDigest md = MessageDigest.getInstance("MD5");
+        md.update(input,offset,length);
+        return md.digest();
+    }
+
+
+
+    /**
+     * Convenience Function: Encrypt MD5 from String to String Hex representation
+     *
+     * @param input
+     * @return
+     * @throws NoSuchAlgorithmException
+     */
+    public static String hashMD5asStringHex(String input) throws NoSuchAlgorithmException {
+        byte[] output = hashMD5(input.getBytes());
+        StringBuilder sb = new StringBuilder("0x");
+         for (byte b : output) {
+            sb.append(hexDigit[(b >> 4) & 0x0f]);
+            sb.append(hexDigit[b & 0x0f]);
+         }
+         return sb.toString();
+    }
+
+/////////////////////////////////
+// SHA256
+/////////////////////////////////
+    /**
+     * SHA256 Hashing
+     */
+    public static byte[] hashSHA256(byte[] input) throws NoSuchAlgorithmException {
+        // Note: Protect against Multi-thread issues with new MessageDigest
+        MessageDigest md = MessageDigest.getInstance("SHA-256");
+        md.update(input);
+        return md.digest();
+    }
+
+    /**
+     * SHA256 Hashing
+     */
+    public static byte[] hashSHA256(byte[] input, int offset, int length) throws NoSuchAlgorithmException {
+        // Note: Protect against Multi-thread issues with new MessageDigest
+        MessageDigest md = MessageDigest.getInstance("SHA-256");
+        md.update(input,offset,length);
+        return md.digest();
+    }
+
+    /**
+     * Convenience Function: Hash from String to String Hex representation
+     *
+     * @param input
+     * @return
+     * @throws NoSuchAlgorithmException
+     */
+    public static String hashSHA256asStringHex(String input) throws NoSuchAlgorithmException {
+        return toHex(hashSHA256(input.getBytes()));
+    }
+
+    /**
+     * Convenience Function: Hash from String to String Hex representation
+     *
+     * @param input
+     * @return
+     * @throws NoSuchAlgorithmException
+     */
+    public static String hashSHA256asStringHex(String input, int salt) throws NoSuchAlgorithmException {
+        byte[] in = input.getBytes();
+        ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + in.length);
+        bb.putInt(salt);
+        bb.put(input.getBytes());
+        return toHex(Hash.hashSHA256(bb.array()));
+    }
+
+    /**
+     * Compare two byte arrays for equivalency
+     * @param ba1
+     * @param ba2
+     * @return
+     */
+    public static boolean isEqual(byte ba1[], byte ba2[]) {
+        if (ba1.length!=ba2.length)return false;
+        for (int i = 0;i<ba1.length; ++i) {
+            if (ba1[i]!=ba2[i])return false;
+        }
+        return true;
+    }
+
+    public static int compareTo(byte[] a, byte[] b) {
+        int end = Math.min(a.length, b.length);
+        int compare = 0;
+        for (int i=0;compare == 0 && i<end;++i) {
+            compare = a[i]-b[i];
+        }
+        if (compare==0)compare=a.length-b.length;
+        return compare;
+    }
+
+    /**
+     * @param ba
+     * @return
+     */
+    public static String toHexNo0x(byte[] ba) {
+        StringBuilder sb = new StringBuilder();
+         for (byte b : ba) {
+            sb.append(hexDigit[(b >> 4) & 0x0f]);
+            sb.append(hexDigit[b & 0x0f]);
+         }
+         return sb.toString();
+    }
+
+    /**
+     * @param ba
+     * @return
+     */
+    public static String toHex(byte[] ba) {
+        StringBuilder sb = new StringBuilder("0x");
+         for (byte b : ba) {
+            sb.append(hexDigit[(b >> 4) & 0x0f]);
+            sb.append(hexDigit[b & 0x0f]);
+         }
+         return sb.toString();
+    }
+
+    public static String toHex(byte[] ba, int start, int length) {
+        StringBuilder sb = new StringBuilder("0x");
+         for (int i=start;i<length;++i) {
+            sb.append(hexDigit[(ba[i] >> 4) & 0x0f]);
+            sb.append(hexDigit[ba[i] & 0x0f]);
+         }
+         return sb.toString();
+    }
+
+
+    public static byte[] fromHex(String s) {
+       if(!s.startsWith("0x")) {
+               return fromHexNo0x(s);
+       }
+        byte b;
+        int c;
+        byte[] ba;
+        int extra = s.length()%2; // odd requires extra
+        ba = new byte[(s.length()-2)/2 + extra];
+        boolean high = extra==0;
+        
+        int idx;
+        for (int i=2;i<s.length();++i) {
+            c = s.charAt(i);
+            if (c>=0x30 && c<=0x39) {
+                b=(byte)(c-0x30);
+            } else if (c>=0x61 && c<=0x66) {
+                b=(byte)(c-0x57);  // account for "A"
+            } else if (c>=0x41 && c<=0x46) {
+                b=(byte)(c-0x37);
+            } else {
+                return null;
+            }
+            idx = (i-2+extra)/2;
+            if (high) {
+                ba[idx]=(byte)(b<<4);
+                high = false;
+            } else {
+                ba[idx]|=b;
+                high = true;
+            }
+        }
+        return ba;
+    }
+    
+    /**
+     * Does not expect to start with "0x"
+     * if Any Character doesn't match, it returns null;
+     *
+     * @param s
+     * @return
+     */
+    public static byte[] fromHexNo0x(String s) {
+        byte b;
+        int c;
+        byte[] ba;
+        int extra = s.length()%2; // odd requires extra byte to store
+        ba = new byte[(s.length())/2 + extra];
+        boolean high = extra==0;
+        
+        int idx;
+        for (int i=0;i<s.length();++i) {
+            c = s.charAt(i);
+            if (c>=0x30 && c<=0x39) {
+                b=(byte)(c-0x30);
+            } else if (c>=0x61 && c<=0x66) {
+                b=(byte)(c-0x57);  // account for "A"
+            } else if (c>=0x41 && c<=0x46) {
+                b=(byte)(c-0x37);
+            } else {
+                return null;
+            }
+            idx = (i+extra)/2;
+            if (high) {
+                ba[idx]=(byte)(b<<4);
+                high = false;
+            } else {
+                ba[idx]|=b;
+                high = true;
+            }
+        }
+        return ba;
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Locator.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Locator.java
new file mode 100644 (file)
index 0000000..ef3ad66
--- /dev/null
@@ -0,0 +1,36 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+public interface Locator<T> {
+    public T get(Locator.Item item) throws LocatorException;
+    public boolean hasItems();
+    public void invalidate(Locator.Item item) throws LocatorException;
+    public Locator.Item best() throws LocatorException;
+    public Item first() throws LocatorException;
+    public Item next(Item item) throws LocatorException;
+    public boolean refresh();
+    public void destroy();
+
+    public interface Item {}
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/LocatorException.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/LocatorException.java
new file mode 100644 (file)
index 0000000..64f0798
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+public class LocatorException extends Exception {
+    /**
+     *
+     */
+    private static final long serialVersionUID = -4267929804321134469L;
+
+    public LocatorException(String arg0) {
+        super(arg0);
+    }
+
+    public LocatorException(Throwable arg0) {
+        super(arg0);
+    }
+
+    public LocatorException(String arg0, Throwable arg1) {
+        super(arg0, arg1);
+    }
+
+    public LocatorException(CharSequence cs) {
+        super(cs.toString());
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Lur.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Lur.java
new file mode 100644 (file)
index 0000000..0c3302f
--- /dev/null
@@ -0,0 +1,91 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+import java.security.Principal;
+import java.util.List;
+
+
+
+/**
+ * LUR: Local User Registry
+ *
+ * Concept by Robert Garskof, Implementation by Jonathan Gathman
+ *
+ * Where we can keep local copies of users and roles for faster Authorization when asked.
+ *
+ * Note: Author cannot resist the mental image of using a Fishing Lure to this LUR pattern
+ *
+ *
+ */
+public interface Lur {
+    /**
+     * Allow the Lur, which has correct Permission access, to create and hand back.
+     */
+    public Permission createPerm(String p);
+
+    /**
+     * Fish for Principals in a Pond
+     *
+     *   or more boringly, is the User identified within a named collection representing permission.
+     *
+     * @param principalName
+     * @return
+     */
+    public boolean fish(Principal bait, Permission ... pond);
+
+    /**
+     * Fish all the Principals out a Pond
+     *
+     *   For additional humor, pronounce the following with a Southern Drawl, "FishOil"
+     *
+     *   or more boringly, load the List with Permissions found for Principal
+     *
+     * @param principalName
+     * @return
+     */
+    public void fishAll(Principal bait, List<Permission> permissions);
+
+    /**
+     * Allow implementations to disconnect, or cleanup resources if unneeded
+     */
+    public void destroy();
+
+    /**
+     * Does this LUR handle this pond exclusively?  Important for EpiLUR to determine whether
+     * to try another (more expensive) LUR
+     * @param pond
+     * @return
+     */
+    public boolean handlesExclusively(Permission ... pond);
+
+    /**
+     *  Does the LUR support a particular kind of Principal
+     *  This can be used to check name's domain, like above, or Principal type
+     */
+    public boolean handles(Principal principal);
+
+    /**
+     * Clear: Clear any Caching, if exists
+     */
+    public void clear(Principal p, StringBuilder report);
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Permission.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Permission.java
new file mode 100644 (file)
index 0000000..37050b9
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+public interface Permission {
+    public String permType();
+    public String getKey();
+    public boolean match(Permission p);
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/PropAccess.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/PropAccess.java
new file mode 100644 (file)
index 0000000..ff9aac6
--- /dev/null
@@ -0,0 +1,437 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk.apps
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ *
+ * Modifications Copyright (C) 2018 IBM.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Properties;
+
+import org.onap.ccsdk.apps.cadi.config.Config;
+import org.onap.ccsdk.apps.cadi.config.SecurityInfo;
+import org.onap.ccsdk.apps.cadi.util.Split;
+
+public class PropAccess implements Access {
+    // Sonar says cannot be static... it's ok.  not too many PropAccesses created.
+    private final SimpleDateFormat iso8601 = newISO8601();
+    private Symm symm;
+    public static final Level DEFAULT = Level.AUDIT;
+    private int level;
+    private Properties props;
+    private List<String> recursionProtection = null;
+    private LogIt logIt;
+    private String name;
+
+    public PropAccess() {
+        logIt = new StreamLogIt(System.out);
+        init(null);
+    }
+
+    /**
+     * This Constructor soly exists to instantiate Servlet Context Based Logging that will call "init" later.
+     * @param sc
+     */
+    protected PropAccess(Object o) {
+        logIt = new StreamLogIt(System.out);
+        props = new Properties();
+    }
+
+    public PropAccess(String ... args) {
+        this(System.out,args);
+    }
+
+    public PropAccess(PrintStream ps, String[] args) {
+        logIt = new StreamLogIt(ps==null?System.out:ps);
+        init(logIt,args);
+    }
+
+    public PropAccess(LogIt logit, String[] args) {
+        init(logit, args);
+    }
+
+    public PropAccess(Properties p) {
+        this(System.out,p);
+    }
+
+    public PropAccess(PrintStream ps, Properties p) {
+        logIt = new StreamLogIt(ps==null?System.out:ps);
+        init(p);
+    }
+
+    protected void init(final LogIt logIt, final String[] args) {
+        this.logIt = logIt;
+        Properties nprops=new Properties();
+        int eq;
+        for (String arg : args) {
+            if ((eq=arg.indexOf('='))>0) {
+                nprops.setProperty(arg.substring(0, eq),arg.substring(eq+1));
+            }
+        }
+        init(nprops);
+    }
+
+    public static SimpleDateFormat newISO8601() {
+        return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
+    }
+
+    protected synchronized void init(Properties p) {
+        // Make sure these two are set before any changes in Logging
+        name = "cadi";
+
+        props = new Properties();
+        // First, load related System Properties
+        for (Entry<Object,Object> es : System.getProperties().entrySet()) {
+            String key = es.getKey().toString();
+            for (String start : new String[] {"cadi_","aaf_","cm_"}) {
+                if (key.startsWith(start)) {
+                    props.put(key, es.getValue());
+                }
+            }
+        }
+        // Second, overlay or fill in with Passed in Props
+        if (p!=null) {
+            props.putAll(p);
+        }
+
+        // Preset LogLevel
+        String sLevel = props.getProperty(Config.CADI_LOGLEVEL);
+        // Third, load any Chained Property Files
+        load(props.getProperty(Config.CADI_PROP_FILES));
+
+        if(sLevel==null) { // if LogLev wasn't set before, check again after Chained Load
+            sLevel = props.getProperty(Config.CADI_LOGLEVEL);
+            if (sLevel==null) {
+                level=DEFAULT.maskOf();
+            } else {
+                level=Level.valueOf(sLevel).maskOf();
+            }
+        }
+        // Setup local Symmetrical key encryption
+        if (symm==null) {
+            try {
+                symm = Symm.obtain(this);
+            } catch (CadiException e) {
+                System.err.append("FATAL ERROR: Cannot obtain Key Information.");
+                e.printStackTrace(System.err);
+                System.exit(1);
+            }
+        }
+
+        name = props.getProperty(Config.CADI_LOGNAME, name);
+
+        SecurityInfo.setHTTPProtocols(this);
+
+    }
+
+
+    private void load(String cadi_prop_files) {
+        if (cadi_prop_files==null) {
+            return;
+        }
+        String prevKeyFile = props.getProperty(Config.CADI_KEYFILE);
+
+
+        for(String filename : Split.splitTrim(File.pathSeparatorChar, cadi_prop_files)) {
+            Properties fileProps = new Properties();
+            File file = new File(filename);
+            if (file.exists()) {
+                printf(Level.INIT,"Loading CADI Properties from %s",file.getAbsolutePath());
+                try {
+                    FileInputStream fis = new FileInputStream(file);
+                    try {
+                        fileProps.load(fis);
+                        // Only load props from recursion which are not already in props
+                        // meaning top Property file takes precedence
+                        for(Entry<Object, Object> es : fileProps.entrySet()) {
+                            if(props.get(es.getKey())==null) {
+                                String key = es.getKey().toString();
+                                String value = es.getValue().toString();
+                                props.put(key, value);
+                                if(key.contains("pass")) {
+                                    value = "vi XX";
+                                }
+                                printf(Level.DEBUG,"  %s=%s",key,value);
+                            }
+                        }
+                        // Recursively Load
+                        String chainProp = fileProps.getProperty(Config.CADI_PROP_FILES);
+                        if (chainProp!=null) {
+                            if (recursionProtection==null) {
+                                recursionProtection = new ArrayList<>();
+                                recursionProtection.add(cadi_prop_files);
+                            }
+                            if (!recursionProtection.contains(chainProp)) {
+                                recursionProtection.add(chainProp);
+                                load(chainProp); // recurse
+                            }
+                        }
+                    } finally {
+                        fis.close();
+                    }
+                } catch (Exception e) {
+                    log(e,filename,"cannot be opened");
+                }
+            } else {
+                printf(Level.WARN,"Warning: recursive CADI Property %s does not exist",file.getAbsolutePath());
+            }
+        }
+
+        // Trim
+        for (Entry<Object, Object> es : props.entrySet()) {
+            Object value = es.getValue();
+            if (value instanceof String) {
+                String trim = ((String)value).trim();
+                // Remove Beginning/End Quotes, which might be there if mixed with Bash Props
+                int s = 0, e=trim.length()-1;
+                if (s<e && trim.charAt(s)=='"' && trim.charAt(e)=='"') {
+                    trim=trim.substring(s+1,e);
+                }
+                if (trim!=value) { // Yes, I want OBJECT equals
+                    props.setProperty((String)es.getKey(), trim);
+                }
+            }
+        }
+        // Reset Symm if Keyfile Changes:
+        String newKeyFile = props.getProperty(Config.CADI_KEYFILE);
+        if ((prevKeyFile!=null && newKeyFile!=null) || (newKeyFile!=null && !newKeyFile.equals(prevKeyFile))) {
+            try {
+                symm = Symm.obtain(this);
+            } catch (CadiException e) {
+                System.err.append("FATAL ERROR: Cannot obtain Key Information.");
+                e.printStackTrace(System.err);
+                System.exit(1);
+            }
+
+            prevKeyFile=newKeyFile;
+        }
+
+        String loglevel = props.getProperty(Config.CADI_LOGLEVEL);
+        if (loglevel!=null) {
+            try {
+                level=Level.valueOf(loglevel).maskOf();
+            } catch (IllegalArgumentException e) {
+                printf(Level.ERROR,"%s=%s is an Invalid Log Level",Config.CADI_LOGLEVEL,loglevel);
+            }
+        }
+    }
+
+    @Override
+    public void load(InputStream is) throws IOException {
+        props.load(is);
+        load(props.getProperty(Config.CADI_PROP_FILES));
+    }
+
+    @Override
+    public void log(Level level, Object ... elements) {
+        if (willLog(level)) {
+            logIt.push(level,elements);
+        }
+    }
+
+    public StringBuilder buildMsg(Level level, Object[] elements) {
+        return buildMsg(name,iso8601,level,elements);
+    }
+
+    /*
+     * Need to pass in DateFormat per thread, because not marked as thread safe
+     */
+    public static StringBuilder buildMsg(final String name, final DateFormat sdf, Level level, Object[] elements) {
+        final StringBuilder sb;
+        int end = elements.length;
+        if(sdf==null) {
+            sb = new StringBuilder();
+            write(true,sb,elements);
+        } else {
+            sb = new StringBuilder(
+                    sdf.format(new Date())
+                    );
+            sb.append(' ');
+            sb.append(level.name());
+            sb.append(" [");
+            sb.append(name);
+            if (end<=0) {
+                sb.append("] ");
+            } else {
+                int idx = 0;
+                if(elements[idx]!=null  &&
+                    elements[idx] instanceof Integer) {
+                    sb.append('-');
+                    sb.append(elements[idx]);
+                    ++idx;
+                }
+                sb.append("] ");
+                write(true,sb,elements);
+            }
+        }
+        return sb;
+    }
+
+    private static boolean write(boolean first, StringBuilder sb, Object[] elements) {
+        String s;
+        for (Object o : elements) {
+            if (o!=null) {
+                if(o.getClass().isArray()) {
+                    first = write(first,sb,(Object[])o);
+                } else if(o instanceof Throwable) {
+                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                    PrintStream ps = new PrintStream(baos);
+                    ((Throwable)o).printStackTrace(ps);
+                    sb.append(baos.toString());
+                } else {
+                    s=o.toString();
+                    if (first) {
+                        first = false;
+                    } else {
+                        int l = s.length();
+                        if (l>0)    {
+                            switch(s.charAt(l-1)) {
+                                case ' ':
+                                    break;
+                                default:
+                                    sb.append(' ');
+                            }
+                        }
+                    }
+                    sb.append(s);
+                }
+            }
+        }
+        return first;
+    }
+
+    @Override
+    public void log(Exception e, Object... elements) {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        pw.println();
+        e.printStackTrace(pw);
+        log(Level.ERROR,elements,sw.toString());
+    }
+
+    @Override
+    public void printf(Level level, String fmt, Object... elements) {
+        if (willLog(level)) {
+            log(level,String.format(fmt, elements));
+        }
+    }
+
+    @Override
+    public void setLogLevel(Level level) {
+        this.level = level.maskOf();
+    }
+
+    @Override
+    public boolean willLog(Level level) {
+        return level.inMask(this.level);
+    }
+
+    @Override
+    public ClassLoader classLoader() {
+        return ClassLoader.getSystemClassLoader();
+    }
+
+    @Override
+    public String getProperty(String tag, String def) {
+        return props.getProperty(tag,def);
+    }
+
+    @Override
+    public String decrypt(String encrypted, boolean anytext) throws IOException {
+        return (encrypted!=null && (anytext==true || encrypted.startsWith(Symm.ENC)))
+            ? symm.depass(encrypted)
+            : encrypted;
+    }
+
+    public String encrypt(String unencrypted) throws IOException {
+        return Symm.ENC+symm.enpass(unencrypted);
+    }
+
+    //////////////////
+    // Additional
+    //////////////////
+    public String getProperty(String tag) {
+        return props.getProperty(tag);
+    }
+
+
+    public Properties getProperties() {
+        return props;
+    }
+
+    public void setProperty(String tag, String value) {
+        if (value!=null) {
+            props.put(tag, value);
+            if (Config.CADI_KEYFILE.equals(tag)) {
+                // reset decryption too
+                try {
+                    symm = Symm.obtain(this);
+                } catch (CadiException e) {
+                    System.err.append("FATAL ERROR: Cannot obtain Key Information.");
+                    e.printStackTrace(System.err);
+                    System.exit(1);
+                }
+            }
+        }
+    }
+
+    public interface LogIt {
+        public void push(Level level, Object ... elements) ;
+    }
+
+    private class StreamLogIt implements LogIt {
+        private PrintStream ps;
+
+        public StreamLogIt(PrintStream ps) {
+            this.ps = ps;
+        }
+        @Override
+        public void push(Level level, Object ... elements) {
+            ps.println(buildMsg(level,elements));
+            ps.flush();
+        }
+    }
+
+    public void set(LogIt logit) {
+        logIt = logit;
+    }
+
+    public void setStreamLogIt(PrintStream ps) {
+        logIt = new StreamLogIt(ps);
+    }
+
+    public String toString() {
+        return props.toString();
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Revalidator.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Revalidator.java
new file mode 100644 (file)
index 0000000..94f77a4
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+@FunctionalInterface
+public interface Revalidator<TRANS> {
+    /**
+     * Re-Validate Credential
+     *
+     * @param prin
+     * @return
+     */
+    public CachedPrincipal.Resp revalidate(TRANS trans, CachedPrincipal prin);
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/SecuritySetter.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/SecuritySetter.java
new file mode 100644 (file)
index 0000000..01a4c82
--- /dev/null
@@ -0,0 +1,43 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+
+/**
+ *  Apply any particular security mechanism
+ *
+ *  This allows the definition of various mechanisms involved outside of DRcli jars
+ *
+ *
+ */
+public interface SecuritySetter<CT> {
+    public String getID();
+
+    public void setSecurity(CT client) throws CadiException;
+
+    /**
+     * Returns number of bad logins registered
+     * @param respCode
+     * @return
+     */
+    public int setLastResponse(int respCode);
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/ServletContextAccess.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/ServletContextAccess.java
new file mode 100644 (file)
index 0000000..cf9a90d
--- /dev/null
@@ -0,0 +1,67 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+import java.util.Enumeration;
+
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletContext;
+
+public class ServletContextAccess extends PropAccess {
+
+    private ServletContext context;
+
+    public ServletContextAccess(FilterConfig filterConfig) {
+        super(filterConfig); // protected constructor... does not have "init" called.
+        context = filterConfig.getServletContext();
+
+        for (Enumeration<?> en = filterConfig.getInitParameterNames();en.hasMoreElements();) {
+            String name = (String)en.nextElement();
+            setProperty(name, filterConfig.getInitParameter(name));
+        }
+        init(getProperties());
+    }
+
+    /* (non-Javadoc)
+     * @see org.onap.ccsdk.apps.cadi.PropAccess#log(org.onap.ccsdk.apps.cadi.Access.Level, java.lang.Object[])
+     */
+    @Override
+    public void log(Level level, Object... elements) {
+        if (willLog(level)) {
+            StringBuilder sb = buildMsg(level, elements);
+            context.log(sb.toString());
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.onap.ccsdk.apps.cadi.PropAccess#log(java.lang.Exception, java.lang.Object[])
+     */
+    @Override
+    public void log(Exception e, Object... elements) {
+        StringBuilder sb = buildMsg(Level.ERROR, elements);
+        context.log(sb.toString(),e);
+    }
+
+    public ServletContext context() {
+        return context;
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Symm.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Symm.java
new file mode 100644 (file)
index 0000000..4822812
--- /dev/null
@@ -0,0 +1,905 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Random;
+
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+
+import org.onap.ccsdk.apps.cadi.Access.Level;
+import org.onap.ccsdk.apps.cadi.config.Config;
+
+/**
+ * Key Conversion, primarily "Base64"
+ *
+ * Base64 is required for "Basic Authorization", which is an important part of the overall CADI Package.
+ *
+ * Note: This author found that there is not a "standard" library for Base64 conversion within Java.
+ * The source code implementations available elsewhere were surprisingly inefficient, requiring, for
+ * instance, multiple string creation, on a transaction pass.  Integrating other packages that might be
+ * efficient enough would put undue Jar File Dependencies given this Framework should have none-but-Java
+ * dependencies.
+ *
+ * The essential algorithm is good for a symmetrical key system, as Base64 is really just
+ * a symmetrical key that everyone knows the values.
+ *
+ * This code is quite fast, taking about .016 ms for encrypting, decrypting and even .08 for key
+ * generation. The speed quality, especially of key generation makes this a candidate for a short term token
+ * used for identity.
+ *
+ * It may be used to easily avoid placing Clear-Text passwords in configurations, etc. and contains
+ * supporting functions such as 2048 keyfile generation (see keygen).  This keyfile should, of course,
+ * be set to "400" (Unix) and protected as any other mechanism requires.
+ *
+ * AES Encryption is also employed to include standards.
+ *
+ * @author Jonathan
+ *
+ */
+public class Symm {
+    private static final byte[] DOUBLE_EQ = new byte[] {'=','='};
+    public static final String ENC = "enc:";
+    private static final Object LOCK = new Object();
+    private static final SecureRandom random = new SecureRandom();
+
+    public final char[] codeset;
+    private final int splitLinesAt;
+    private final String encoding;
+    private final Convert convert;
+    private final boolean endEquals;
+    private byte[] keyBytes = null;
+    //Note: AES Encryption is not Thread Safe.  It is Synchronized
+    //private AES aes = null;  // only initialized from File, and only if needed for Passwords
+    private String name;
+
+    /**
+     * This is the standard base64 Key Set.
+     * RFC 2045
+     */
+    public static final Symm base64 = new Symm(
+            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray()
+            ,76, Config.UTF_8,true, "Base64");
+
+    public static final Symm base64noSplit = new Symm(
+            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray()
+            ,Integer.MAX_VALUE, Config.UTF_8,true, "Base64, no Split");
+
+    /**
+     * This is the standard base64 set suitable for URLs and Filenames
+     * RFC 4648
+     */
+    public static final Symm base64url = new Symm(
+            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".toCharArray()
+            ,76, Config.UTF_8,true, "Base64 for URL");
+
+    /**
+     * A Password set, using US-ASCII
+     * RFC 4648
+     */
+    public static final Symm encrypt = new Symm(base64url.codeset,1024, "US-ASCII", false, "Base64, 1024 size");
+    private static final byte[] EMPTY = new byte[0];
+
+    /**
+     * A typical set of Password Chars
+     * Note, this is too large to fit into the algorithm. Only use with PassGen
+     */
+    private static char passChars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+!@#$%^&*(){}[]?:;,.".toCharArray();
+
+
+    private static Symm internalOnly = null;
+
+    /**
+     * Use this to create special case Case Sets and/or Line breaks
+     *
+     * If you don't know why you need this, use the Singleton Method
+     *
+     * @param codeset
+     * @param split
+     */
+    public Symm(char[] codeset, int split, String charset, boolean useEndEquals, String name) {
+        this.codeset = codeset;
+        splitLinesAt = split;
+        encoding = charset;
+        endEquals = useEndEquals;
+        this.name = name;
+        char prev = 0, curr=0, first = 0;
+        int offset=Integer.SIZE; // something that's out of range for integer array
+
+        // There can be time efficiencies gained when the underlying keyset consists mainly of ordered
+        // data (i.e. abcde...).  Therefore, we'll quickly analyze the keyset.  If it proves to have
+        // too much entropy, the "Unordered" algorithm, which is faster in such cases is used.
+        ArrayList<int[]> la = new ArrayList<>();
+        for (int i=0;i<codeset.length;++i) {
+            curr = codeset[i];
+            if (prev+1==curr) { // is next character in set
+                prev = curr;
+            } else {
+                if (offset!=Integer.SIZE) { // add previous range
+                    la.add(new int[]{first,prev,offset});
+                }
+                first = prev = curr;
+                offset = curr-i;
+            }
+        }
+        la.add(new int[]{first,curr,offset});
+        if (la.size()>codeset.length/3) {
+            convert = new Unordered(codeset);
+        } else { // too random to get speed enhancement from range algorithm
+            int[][] range = new int[la.size()][];
+            la.toArray(range);
+            convert = new Ordered(range);
+        }
+    }
+
+    public Symm copy(int lines) {
+        return new Symm(codeset,lines,encoding,endEquals, "Copied " + lines);
+    }
+
+    // Only used by keygen, which is intentionally randomized. Therefore, always use unordered
+    private  Symm(char[] codeset, Symm parent) {
+        this.codeset = codeset;
+        splitLinesAt = parent.splitLinesAt;
+        endEquals = parent.endEquals;
+        encoding = parent.encoding;
+        convert = new Unordered(codeset);
+    }
+
+    /**
+     * Obtain the base64() behavior of this class, for use in standard BASIC AUTH mechanism, etc.
+     * @return
+     */
+    @Deprecated
+    public static final Symm base64() {
+        return base64;
+    }
+
+    /**
+     * Obtain the base64() behavior of this class, for use in standard BASIC AUTH mechanism, etc.
+     * No Line Splitting
+     * @return
+     */
+    @Deprecated
+    public static final Symm base64noSplit() {
+        return base64noSplit;
+    }
+
+    /**
+     * Obtain the base64 "URL" behavior of this class, for use in File Names, etc. (no "/")
+     */
+    @Deprecated
+    public static final Symm base64url() {
+        return base64url;
+    }
+
+    /**
+     * Obtain a special ASCII version for Scripting, with base set of base64url use in File Names, etc. (no "/")
+     */
+    public static final Symm baseCrypt() {
+        return encrypt;
+    }
+
+    public <T> T exec(SyncExec<T> exec) throws Exception {
+        synchronized(LOCK) {
+            if (keyBytes == null) {
+                keyBytes = new byte[AES.AES_KEY_SIZE/8];
+                int offset = (Math.abs(codeset[0])+47)%(codeset.length-keyBytes.length);
+                for (int i=0;i<keyBytes.length;++i) {
+                    keyBytes[i] = (byte)codeset[i+offset];
+                }
+            }
+        }
+        return exec.exec(new AES(keyBytes,0,keyBytes.length));
+    }
+
+    public interface Encryption {
+        public CipherOutputStream outputStream(OutputStream os, boolean encrypt);
+        public CipherInputStream inputStream(InputStream is, boolean encrypt);
+    }
+
+    public static interface SyncExec<T> {
+        public T exec(Encryption enc) throws IOException, Exception;
+    }
+
+    public byte[] encode(byte[] toEncrypt) throws IOException {
+            if (toEncrypt==null) {
+                return EMPTY;
+            } else {
+            ByteArrayOutputStream baos = new ByteArrayOutputStream((int)(toEncrypt.length*1.25));
+            encode(new ByteArrayInputStream(toEncrypt),baos);
+            return baos.toByteArray();
+            }
+    }
+
+    public byte[] decode(byte[] encrypted) throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream((int)(encrypted.length*1.25));
+        decode(new ByteArrayInputStream(encrypted),baos);
+        return baos.toByteArray();
+    }
+
+    /**
+     *  Helper function for String API of "Encode"
+     *  use "getBytes" with appropriate char encoding, etc.
+     *
+     * @param str
+     * @return
+     * @throws IOException
+     */
+    public String encode(String str) throws IOException {
+        byte[] array;
+        boolean useDefaultEncoding = false;
+        try {
+            array = str.getBytes(encoding);
+        } catch (IOException e) {
+            array = str.getBytes(); // take default
+            useDefaultEncoding = true;
+        }
+        // Calculate expected size to avoid any buffer expansion copies within the ByteArrayOutput code
+        ByteArrayOutputStream baos = new ByteArrayOutputStream((int)(array.length*1.363)); // account for 4 bytes for 3 and a byte or two more
+
+        encode(new ByteArrayInputStream(array),baos);
+        if (useDefaultEncoding) {
+            return baos.toString();
+        }
+        return baos.toString(encoding);
+    }
+
+    /**
+     * Helper function for the String API of "Decode"
+     * use "getBytes" with appropriate char encoding, etc.
+     * @param str
+     * @return
+     * @throws IOException
+     */
+    public String decode(String str) throws IOException {
+        byte[] array;
+        boolean useDefaultEncoding = false;
+        try {
+            array = str.getBytes(encoding);
+        } catch (IOException e) {
+            array = str.getBytes(); // take default
+            useDefaultEncoding = true;
+        }
+        // Calculate expected size to avoid any buffer expansion copies within the ByteArrayOutput code
+        ByteArrayOutputStream baos = new ByteArrayOutputStream((int)(array.length*.76)); // Decoding is 3 bytes for 4.  Allocate slightly more than 3/4s
+        decode(new ByteArrayInputStream(array), baos);
+        if (useDefaultEncoding) {
+            return baos.toString();
+        }
+        return baos.toString(encoding);
+    }
+
+    /**
+     * Convenience Function
+     *
+     * encode String into InputStream and call encode(InputStream, OutputStream)
+     *
+     * @param string
+     * @param out
+     * @throws IOException
+     */
+      public void encode(String string, OutputStream out) throws IOException {
+          encode(new ByteArrayInputStream(string.getBytes()),out);
+    }
+
+    /**
+     * Convenience Function
+     *
+     * encode String into InputStream and call decode(InputStream, OutputStream)
+     *
+     * @param string
+     * @param out
+     * @throws IOException
+     */
+    public void decode(String string, OutputStream out) throws IOException {
+        decode(new ByteArrayInputStream(string.getBytes()),out);
+    }
+
+    public void encode(InputStream is, OutputStream os, byte[] prefix) throws IOException {
+        os.write(prefix);
+        encode(is,os);
+    }
+
+    /**
+     * encode InputStream onto Output Stream
+     *
+     * @param is
+     * @param estimate
+     * @return
+     * @throws IOException
+     */
+    public void encode(InputStream is, OutputStream os) throws IOException {
+        // StringBuilder sb = new StringBuilder((int)(estimate*1.255)); // try to get the right size of StringBuilder from start.. slightly more than 1.25 times
+        int prev=0;
+        int read, idx=0, line=0;
+        boolean go;
+        do {
+            read = is.read();
+            if (go = read>=0) {
+                if (line>=splitLinesAt) {
+                    os.write('\n');
+                    line = 0;
+                }
+                switch(++idx) { // 1 based reading, slightly faster ++
+                    case 1: // ptr is the first 6 bits of read
+                        os.write(codeset[read>>2]);
+                        prev = read;
+                        break;
+                    case 2: // ptr is the last 2 bits of prev followed by the first 4 bits of read
+                        os.write(codeset[((prev & 0x03)<<4) | (read>>4)]);
+                        prev = read;
+                        break;
+                    default: //(3+)
+                            // Char 1 is last 4 bits of prev plus the first 2 bits of read
+                            // Char 2 is the last 6 bits of read
+                        os.write(codeset[(((prev & 0xF)<<2) | (read>>6))]);
+                        if (line==splitLinesAt) { // deal with line splitting for two characters
+                            os.write('\n');
+                            line=0;
+                        }
+                        os.write(codeset[(read & 0x3F)]);
+                        ++line;
+                        idx = 0;
+                        prev = 0;
+                }
+                ++line;
+            } else { // deal with any remaining bits from Prev, then pad
+                switch(idx) {
+                    case 1: // just the last 2 bits of prev
+                        os.write(codeset[(prev & 0x03)<<4]);
+                        if (endEquals)os.write(DOUBLE_EQ);
+                        break;
+                    case 2: // just the last 4 bits of prev
+                        os.write(codeset[(prev & 0xF)<<2]);
+                        if (endEquals)os.write('=');
+                        break;
+                }
+                idx = 0;
+            }
+
+        } while (go);
+    }
+
+    public void decode(InputStream is, OutputStream os, int skip) throws IOException {
+        if (is.skip(skip)!=skip) {
+            throw new IOException("Error skipping on IOStream in Symm");
+        }
+        decode(is,os);
+    }
+
+    /**
+     * Decode InputStream onto OutputStream
+     * @param is
+     * @param os
+     * @throws IOException
+     */
+    public void decode(InputStream is, OutputStream os) throws IOException {
+       int read, idx=0;
+       int prev=0, index;
+           while ((read = is.read())>=0) {
+               index = convert.convert(read);
+               if (index>=0) {
+                switch(++idx) { // 1 based cases, slightly faster ++
+                    case 1: // index goes into first 6 bits of prev
+                        prev = index<<2;
+                        break;
+                    case 2: // write second 2 bits of into prev, write byte, last 4 bits go into prev
+                        os.write((byte)(prev|(index>>4)));
+                        prev = index<<4;
+                        break;
+                    case 3: // first 4 bits of index goes into prev, write byte, last 2 bits go into prev
+                        os.write((byte)(prev|(index>>2)));
+                        prev = index<<6;
+                        break;
+                    default: // (3+) | prev and last six of index
+                        os.write((byte)(prev|(index&0x3F)));
+                        idx = prev = 0;
+                }
+               }
+           };
+           os.flush();
+   }
+
+   /**
+    * Interface to allow this class to choose which algorithm to find index of character in Key
+    * @author Jonathan
+    *
+    */
+   private interface Convert {
+       public int convert(int read) throws IOException;
+   }
+
+   /**
+    * Ordered uses a range of orders to compare against, rather than requiring the investigation
+    * of every character needed.
+    * @author Jonathan
+    *
+    */
+   private static final class Ordered implements Convert {
+       private int[][] range;
+       public Ordered(int[][] range) {
+           this.range = range;
+       }
+       public int convert(int read) throws IOException {
+           // System.out.print((char)read);
+           switch(read) {
+               case -1:
+               case '=':
+               case ' ':
+               case '\n':
+               case '\r':
+                   return -1;
+           }
+           for (int i=0;i<range.length;++i) {
+               if (read >= range[i][0] && read<=range[i][1]) {
+                   return read-range[i][2];
+               }
+           }
+           throw new IOException("Unacceptable Character in Stream");
+       }
+   }
+
+   /**
+    * Unordered, i.e. the key is purposely randomized, simply has to investigate each character
+    * until we find a match.
+    * @author Jonathan
+    *
+    */
+   private static final class Unordered implements Convert {
+       private char[] codec;
+       public Unordered(char[] codec) {
+           this.codec = codec;
+       }
+       public int convert(int read) throws IOException {
+           switch(read) {
+               case -1:
+               case '=':
+                 case '\n':
+                 case '\r':
+                   return -1;
+           }
+           for (int i=0;i<codec.length;++i) {
+               if (codec[i]==read)return i;
+           }
+          // don't give clue in Encryption mode
+          throw new IOException("Unacceptable Character in Stream");
+       }
+   }
+
+   /**
+    * Generate a 2048 based Key from which we extract our code base
+    *
+    * @return
+    * @throws IOException
+    */
+   public static byte[] keygen() throws IOException {
+        byte inkey[] = new byte[0x600];
+        new SecureRandom().nextBytes(inkey);
+        ByteArrayOutputStream baos = new ByteArrayOutputStream(0x800);
+        base64url.encode(new ByteArrayInputStream(inkey), baos);
+        return baos.toByteArray();
+   }
+
+   // A class allowing us to be less predictable about significant digits (i.e. not picking them up from the
+   // beginning, and not picking them up in an ordered row.  Gives a nice 2048 with no visible patterns.
+   private class Obtain {
+       private int last;
+       private int skip;
+       private int length;
+       private byte[] key;
+
+       private Obtain(Symm b64, byte[] key) {
+           skip = Math.abs(key[key.length-13]%key.length);
+           if ((key.length&0x1) == (skip&0x1)) { // if both are odd or both are even
+               ++skip;
+           }
+           length = b64.codeset.length;
+           last = 17+length%59; // never start at beginning
+           this.key = key;
+       }
+
+       private int next() {
+             return Math.abs(key[(++last*skip)%key.length])%length;
+       }
+   };
+
+   /**
+    * Obtain a Symm from "keyfile" (Config.KEYFILE) property
+    *
+    * @param acesss
+    * @return
+ * @throws IOException
+ * @throws CadiException
+    */
+   public static Symm obtain(Access access) throws CadiException {
+        String keyfile = access.getProperty(Config.CADI_KEYFILE,null);
+        if (keyfile!=null) {
+            Symm symm = Symm.baseCrypt();
+
+            File file = new File(keyfile);
+            try {
+                access.log(Level.INIT, Config.CADI_KEYFILE,"points to",file.getCanonicalPath());
+            } catch (IOException e1) {
+                access.log(Level.INIT, Config.CADI_KEYFILE,"points to",file.getAbsolutePath());
+            }
+            if (file.exists()) {
+                try {
+                    FileInputStream fis = new FileInputStream(file);
+                    try {
+                        symm = Symm.obtain(fis);
+                    } finally {
+                        try {
+                           fis.close();
+                        } catch (IOException e) {
+                        }
+                    }
+                } catch (IOException e) {
+                    access.log(e, "Cannot load keyfile");
+                }
+            } else {
+                String filename;
+                try {
+                    filename = file.getCanonicalPath();
+                } catch (IOException e) {
+                    filename = file.getAbsolutePath();
+                }
+                throw new CadiException("ERROR: " + filename + " does not exist!");
+            }
+            return symm;
+        } else {
+            try {
+                return internalOnly();
+            } catch (IOException e) {
+                throw new CadiException(e);
+            }
+        }
+   }
+  /**
+   *  Create a new random key
+   */
+  public Symm obtain() throws IOException {
+        byte inkey[] = new byte[0x800];
+        new SecureRandom().nextBytes(inkey);
+        Symm s = obtain(inkey);
+        s.name = "from Random";
+        return s;
+  }
+
+  /**
+   * Obtain a Symm from 2048 key from a String
+   *
+   * @param key
+   * @return
+   * @throws IOException
+   */
+  public static Symm obtain(String key) throws IOException {
+      Symm s = obtain(new ByteArrayInputStream(key.getBytes()));
+      s.name = "from String";
+      return s;
+  }
+
+  /**
+   * Obtain a Symm from 2048 key from a Stream
+   *
+   * @param is
+   * @return
+   * @throws IOException
+   */
+  public static Symm obtain(InputStream is) throws IOException {
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      try {
+          base64url.decode(is, baos);
+      } catch (IOException e) {
+          // don't give clue
+          throw new IOException("Invalid Key");
+      }
+      byte[] bkey = baos.toByteArray();
+      if (bkey.length<0x88) { // 2048 bit key
+          throw new IOException("Invalid key");
+      }
+      Symm s = baseCrypt().obtain(bkey);
+      s.name = "from InputStream";
+      return s;
+  }
+
+  /**
+   * Convenience for picking up Keyfile
+   *
+   * @param f
+   * @return
+   * @throws IOException
+   */
+  public static Symm obtain(File f) throws IOException {
+      FileInputStream fis = new FileInputStream(f);
+      try {
+          Symm s = obtain(fis);
+          s.name = "From " + f.getCanonicalPath() + " dated " + new Date(f.lastModified());
+          return s;
+      } finally {
+          fis.close();
+      }
+  }
+  /**
+   * Decrypt into a String
+   *
+   *  Convenience method
+   *
+   * @param password
+   * @return
+   * @throws IOException
+   */
+  public String enpass(String password) throws IOException {
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      enpass(password,baos);
+      return new String(baos.toByteArray());
+  }
+
+  /**
+   * Create an encrypted password, making sure that even short passwords have a minimum length.
+   *
+   * @param password
+   * @param os
+   * @throws IOException
+   */
+  public void enpass(final String password, final OutputStream os) throws IOException {
+        if (password==null) {
+            throw new IOException("Invalid password passed");
+        }
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        DataOutputStream dos = new DataOutputStream(baos);
+        byte[] bytes = password.getBytes();
+        if (this.getClass().getSimpleName().startsWith("base64")) { // don't expose randomization
+            dos.write(bytes);
+        } else {
+
+            Random r = new SecureRandom();
+            int start = 0;
+            byte b;
+            for (int i=0;i<3;++i) {
+                dos.writeByte(b=(byte)r.nextInt());
+                start+=Math.abs(b);
+            }
+            start%=0x7;
+            for (int i=0;i<start;++i) {
+                dos.writeByte(r.nextInt());
+            }
+            dos.writeInt((int)System.currentTimeMillis());
+            int minlength = Math.min(0x9,bytes.length);
+            dos.writeByte(minlength); // expect truncation
+            if (bytes.length<0x9) {
+                for (int i=0;i<bytes.length;++i) {
+                    dos.writeByte(r.nextInt());
+                    dos.writeByte(bytes[i]);
+                }
+                // make sure it's long enough
+                for (int i=bytes.length;i<0x9;++i) {
+                    dos.writeByte(r.nextInt());
+                }
+            } else {
+                dos.write(bytes);
+            }
+        }
+
+        // 7/21/2016 Jonathan add AES Encryption to the mix
+        try {
+            exec(new SyncExec<Void>() {
+                @Override
+                public Void exec(Encryption enc) throws Exception {
+                    CipherInputStream cis = enc.inputStream(new ByteArrayInputStream(baos.toByteArray()), true);
+                    try {
+                        encode(cis,os);
+                    } finally {
+                        os.flush();
+                        cis.close();
+                    }
+                    return null;
+                }
+            });
+        } catch (IOException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new IOException(e);
+        }
+    }
+
+  /**
+   * Decrypt a password into a String
+   *
+   * Convenience method
+   *
+   * @param password
+   * @return
+   * @throws IOException
+   */
+  public String depass(String password) throws IOException {
+      if (password==null)return null;
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      depass(password,baos);
+      return new String(baos.toByteArray());
+  }
+
+  /**
+   * Decrypt a password
+   *
+   * Skip Symm.ENC
+   *
+   * @param password
+   * @param os
+   * @return
+   * @throws IOException
+   */
+  public long depass(final String password, final OutputStream os) throws IOException {
+      int offset = password.startsWith(ENC)?4:0;
+      final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      final ByteArrayInputStream bais =  new ByteArrayInputStream(password.getBytes(),offset,password.length()-offset);
+      try {
+        exec(new SyncExec<Void>() {
+            @Override
+            public Void exec(Encryption enc) throws IOException {
+                  CipherOutputStream cos = enc.outputStream(baos, false);
+                  decode(bais,cos);
+                  cos.close(); // flush
+                  return null;
+            }
+          });
+        } catch (IOException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new IOException(e);
+        }
+
+      byte[] bytes = baos.toByteArray();
+      DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes));
+      long time;
+      if (this.getClass().getSimpleName().startsWith("base64")) { // don't expose randomization
+          os.write(bytes);
+          time = 0L;
+      } else {
+          int start=0;
+          for (int i=0;i<3;++i) {
+              start+=Math.abs(dis.readByte());
+          }
+          start%=0x7;
+          for (int i=0;i<start;++i) {
+              dis.readByte();
+          }
+          time = (dis.readInt() & 0xFFFF)|(System.currentTimeMillis()&0xFFFF0000);
+          int minlength = dis.readByte();
+          if (minlength<0x9){
+            DataOutputStream dos = new DataOutputStream(os);
+            for (int i=0;i<minlength;++i) {
+                dis.readByte();
+                dos.writeByte(dis.readByte());
+            }
+          } else {
+              int pre =((Byte.SIZE*3+Integer.SIZE+Byte.SIZE)/Byte.SIZE)+start;
+              os.write(bytes, pre, bytes.length-pre);
+          }
+      }
+      return time;
+  }
+
+  public static String randomGen(int numBytes) {
+      return randomGen(passChars,numBytes);
+  }
+
+  public static String randomGen(char[] chars ,int numBytes) {
+        int rint;
+        StringBuilder sb = new StringBuilder(numBytes);
+        for (int i=0;i<numBytes;++i) {
+            rint = random.nextInt(chars.length);
+            sb.append(chars[rint]);
+        }
+        return sb.toString();
+  }
+  // Internal mechanism for helping to randomize placement of characters within a Symm codeset
+  // Based on an incoming data stream (originally created randomly, but can be recreated within
+  // 2048 key), go after a particular place in the new codeset.  If that codeset spot is used, then move
+  // right or left (depending on iteration) to find the next available slot.  In this way, key generation
+  // is speeded up by only enacting N iterations, but adds a spreading effect of the random number stream, so that keyset is also
+  // shuffled for a good spread. It is, however, repeatable, given the same number set, allowing for
+  // quick recreation when the official stream is actually obtained.
+  public Symm obtain(byte[] key) throws IOException {
+      int filled = codeset.length;
+      char[] seq = new char[filled];
+      int end = filled--;
+
+      boolean right = true;
+      int index;
+      Obtain o = new Obtain(this,key);
+
+      while (filled>=0) {
+          index = o.next();
+          if (index<0 || index>=codeset.length) {
+              System.out.println("uh, oh");
+          }
+          if (right) { // alternate going left or right to find the next open slot (keeps it from taking too long to hit something)
+              for (int j=index;j<end;++j) {
+                  if (seq[j]==0) {
+                      seq[j]=codeset[filled];
+                      --filled;
+                      break;
+                  }
+              }
+              right = false;
+          } else {
+              for (int j=index;j>=0;--j) {
+                  if (seq[j]==0) {
+                      seq[j]=codeset[filled];
+                      --filled;
+                      break;
+                  }
+              }
+              right = true;
+          }
+      }
+      Symm newSymm = new Symm(seq,this);
+      newSymm.name = "from bytes";
+      // Set the KeyBytes
+      try {
+          newSymm.keyBytes = new byte[AES.AES_KEY_SIZE/8];
+          int offset = (Math.abs(key[(47%key.length)])+137)%(key.length-newSymm.keyBytes.length);
+          for (int i=0;i<newSymm.keyBytes.length;++i) {
+              newSymm.keyBytes[i] = key[i+offset];
+          }
+      } catch (Exception e) {
+          throw new IOException(e);
+      }
+
+      return newSymm;
+  }
+
+  /**
+   * This Symm is generated for internal JVM use.  It has no external keyfile, but can be used
+   * for securing Memory, as it remains the same ONLY of the current JVM
+   * @return
+ * @throws IOException
+   */
+  public static synchronized Symm internalOnly() throws IOException {
+      if (internalOnly==null) {
+          ByteArrayInputStream baos = new ByteArrayInputStream(keygen());
+          try {
+              internalOnly = Symm.obtain(baos);
+          } finally {
+              baos.close();
+          }
+      }
+      return internalOnly;
+  }
+
+  @Override
+  public String toString() {
+      return name;
+  }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Taf.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Taf.java
new file mode 100644 (file)
index 0000000..c273e68
--- /dev/null
@@ -0,0 +1,57 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+import org.onap.ccsdk.apps.cadi.taf.TafResp;
+
+
+/**
+ * TAF - Transmutative Assertion Framework.
+ *
+ * This main Interface embodies the essential of the assertion, where a number of different TAFs might be used to authenticate
+ * and that authentication to be recognized through other elements.
+ *
+ * Concept by Robert Garskof.  Implemented by Jonathan Gathman
+ *
+ * @author Jonathan
+ *
+ */
+public interface Taf {
+    enum LifeForm {CBLF, SBLF, LFN};
+    /**
+     * The lifeForm param is a humorous way of describing whether the interaction is proceeding from direct Human Interaction via a browser
+     * or App which can directly query a memorized password, key sequence, bio-feedback, from that user, or a machine mechanism for which identity
+     * can more easily be determined by Certificate, Mechanical ID/Password etc.  Popularized in modern culture and Science Fiction (especially
+     * Star Trek), we (starting with Robert Garskof) use the terms "Carbon Based Life Form" (CBLF) for mechanisms with people at the end of them, or
+     * "Silicon Based Life Forms" (SBLF) to indicate machine only interactions.  I have added "LFN" for (Life-Form Neutral) to aid identifying
+     * processes for which it doesn't matter whether there is a human at the immediate end of the chain, or cannot be determined mechanically.
+     *
+     * The variable parameter is not necessarily ideal, but with too many unknown Tafs to be created, flexibility,
+     * is unfortunately required at this point.  Future versions could lock this down more.  Jonathan 10/18/2012
+     *
+     * @param lifeForm
+     * @param info
+     * @return
+     */
+    public TafResp validate(LifeForm reading, String ... info);
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Transmutate.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/Transmutate.java
new file mode 100644 (file)
index 0000000..1e8f7c6
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+import java.security.Principal;
+
+/**
+ * The unique element of TAF is that we establish the relationship/mechanism to mutate the Principal derived from
+ * one Authentication mechanism into a trustable Principal of another.  The mechanism needs to be decided by system
+ * trusting.
+ *
+ * The Generic "T" is used so that the code used will be very specific for the implementation, enforced by Compiler
+ *
+ * This interface will allow differences of trusting Transmutation of Authentication
+ * @author Jonathan
+ *
+ */
+public interface Transmutate<T> {
+    /**
+     * Mutate the (assumed validated) Principal into the expected Principal name to be used to construct
+     *
+     * @param p
+     * @return
+     */
+    public T mutate(Principal p);
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/TrustChecker.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/TrustChecker.java
new file mode 100644 (file)
index 0000000..8fae064
--- /dev/null
@@ -0,0 +1,52 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+import org.onap.ccsdk.apps.cadi.taf.TafResp;
+
+/**
+ * Change to another Principal based on Trust of caller and User Chain (if desired)
+ *
+ * @author Jonathan
+ *
+ */
+public interface TrustChecker {
+    public TafResp mayTrust(TafResp tresp, HttpServletRequest req);
+
+    /**
+     * A class that trusts no-one else, so just return same TResp
+     */
+    public static TrustChecker NOTRUST = new TrustChecker() {
+        @Override
+        public TafResp mayTrust(TafResp tresp, HttpServletRequest req) {
+            return tresp;
+        }
+
+        @Override
+        public void setLur(Lur lur) {
+        }
+    };
+
+    public void setLur(Lur lur);
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/User.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/User.java
new file mode 100644 (file)
index 0000000..d4aa3f9
--- /dev/null
@@ -0,0 +1,176 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+import java.security.Principal;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.onap.ccsdk.apps.cadi.lur.LocalPermission;
+
+/**
+ * Class to hold info from the User Perspective.
+ *
+ *
+ */
+public final class User<PERM extends Permission> {
+    private static final Map<String,Permission> NULL_MAP = new HashMap<>();
+    public String name;
+    private byte[] cred;
+    public Principal principal;
+    Map<String, Permission> perms;
+    long permExpires;
+    private final long interval;
+    int count;
+
+    // Note: This should only be used for Local RBAC (in memory)
+    public User(Principal principal) {
+        this.principal = principal;
+        name = principal.getName();
+        perms = NULL_MAP;
+        permExpires = Long.MAX_VALUE; // Never.  Well, until 64 bits of millis since 1970 expires...
+        interval = 0L;
+        count = 0;
+    }
+
+    public User(String name, byte[] cred) {
+        this.principal = null;
+        this.name = name;
+        this.cred = cred;
+        perms = NULL_MAP;
+        permExpires = Long.MAX_VALUE; // Never.  Well, until 64 bits of millis since 1970 expires...
+        interval = 0L;
+        count = 0;
+    }
+
+    public User(Principal principal, long expireInterval) {
+        this.principal = principal;
+        this.name = principal.getName();
+        perms = NULL_MAP;
+        expireInterval = Math.max(expireInterval, 0); // avoid < 1
+        interval = Math.max(AbsUserCache.MIN_INTERVAL,Math.min(expireInterval,AbsUserCache.MAX_INTERVAL));
+        count = 0;
+        renewPerm();
+        renewPerm();
+    }
+
+    public User(String name, byte[] cred, long expireInterval) {
+        this.principal = null;
+        this.name = name;
+        this.cred = cred;
+        perms = NULL_MAP;
+        expireInterval = Math.max(expireInterval, 0); // avoid < 1
+        interval = Math.max(AbsUserCache.MIN_INTERVAL,Math.min(expireInterval,AbsUserCache.MAX_INTERVAL));
+        count = 0;
+        renewPerm();
+    }
+
+    public void renewPerm() {
+        permExpires = System.currentTimeMillis()+interval;
+    }
+
+    public long permExpires() {
+        return permExpires;
+    }
+
+    public boolean permExpired() {
+        return System.currentTimeMillis() > permExpires;
+    }
+
+    public boolean noPerms() {
+        return perms==null || perms==NULL_MAP || perms.values().size()==0;
+    }
+
+    public synchronized void setNoPerms() {
+        perms=NULL_MAP;
+        renewPerm();
+    }
+
+    public boolean permsUnloaded() {
+        return perms==null || perms==NULL_MAP;
+    }
+
+    public synchronized void incCount() {
+        ++count;
+    }
+
+    public synchronized void resetCount() {
+        count=0;
+    }
+
+    public Map<String,Permission> newMap() {
+        return new ConcurrentHashMap<>();
+    }
+
+    public void add(LocalPermission permission) {
+        if (perms==NULL_MAP) {
+            perms=newMap();
+        }
+        perms.put(permission.getKey(),permission);
+    }
+
+    public void add(Map<String, Permission> newMap, PERM permission) {
+        newMap.put(permission.getKey(),permission);
+    }
+
+    public synchronized void setMap(Map<String, Permission> newMap) {
+        perms = newMap;
+        renewPerm();
+    }
+
+    public boolean contains(Permission perm) {
+        for (Permission p : perms.values()) {
+            if (p.match(perm)) return true;
+        }
+        return false;
+    }
+
+    public void copyPermsTo(List<Permission> sink) {
+        sink.addAll(perms.values());
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(principal.getName());
+        sb.append('|');
+        boolean first = true;
+        synchronized(perms) {
+            for (Permission gp : perms.values()) {
+                if (first) {
+                    first = false;
+                    sb.append(':');
+                } else {
+                    sb.append(',');
+                }
+                sb.append(gp.getKey());
+            }
+        }
+        return sb.toString();
+    }
+
+    public byte[] getCred() {
+        return cred;
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/UserChain.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/UserChain.java
new file mode 100644 (file)
index 0000000..1e1fc32
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi;
+
+/**
+ * Interface to add a User Chain String to Principal
+ *
+ *
+ *
+ *  Where
+ *  APP is name suitable for Logging (i.e. official App Acronym)
+ *  ID is official User or MechID, best if includes Identity Source (i.e. ab1234@people.osaaf.org)
+ *  Protocol is the Security protocol,
+ *
+ *  Format:<ID>:<APP>:<protocol>[:AS][,<ID>:<APP>:<protocol>]*
+ *
+ *
+ *
+ */
+public interface UserChain  {
+    public enum Protocol {BasicAuth,Cookie,Cert,OAuth};
+    public String userChain();
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/Config.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/Config.java
new file mode 100644 (file)
index 0000000..b50bfc2
--- /dev/null
@@ -0,0 +1,1078 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.config;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.UnknownHostException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.onap.ccsdk.apps.cadi.AbsUserCache;
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.Access.Level;
+import org.onap.ccsdk.apps.cadi.CachingLur;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.Connector;
+import org.onap.ccsdk.apps.cadi.CredVal;
+import org.onap.ccsdk.apps.cadi.CredValDomain;
+import org.onap.ccsdk.apps.cadi.Locator;
+import org.onap.ccsdk.apps.cadi.LocatorException;
+import org.onap.ccsdk.apps.cadi.Lur;
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.Symm;
+import org.onap.ccsdk.apps.cadi.TrustChecker;
+import org.onap.ccsdk.apps.cadi.lur.EpiLur;
+import org.onap.ccsdk.apps.cadi.lur.LocalLur;
+import org.onap.ccsdk.apps.cadi.lur.NullLur;
+import org.onap.ccsdk.apps.cadi.taf.HttpEpiTaf;
+import org.onap.ccsdk.apps.cadi.taf.HttpTaf;
+import org.onap.ccsdk.apps.cadi.taf.basic.BasicHttpTaf;
+import org.onap.ccsdk.apps.cadi.taf.cert.X509Taf;
+import org.onap.ccsdk.apps.cadi.taf.dos.DenialOfServiceTaf;
+import org.onap.ccsdk.apps.cadi.util.FixURIinfo;
+import org.onap.ccsdk.apps.cadi.util.Split;
+
+/**
+ * Create a Consistent Configuration mechanism, even when configuration styles
+ * are as vastly different as Properties vs JavaBeans vs FilterConfigs...
+ *
+ * @author Jonathan
+ *
+ */
+public class Config {
+
+    private static final String AAF_V2_0 = "org.onap.aaf.cadi.aaf.v2_0";
+    private static final String AAF_V2_0_AAFCON = AAF_V2_0 + ".AAFCon";
+    private static final String AAF_V2_0_AAF_LUR_PERM = AAF_V2_0 + ".AAFLurPerm";
+    public static final String AAF_V2_0_AAF_CON_HTTP = AAF_V2_0 + ".AAFConHttp";
+
+    private static final String OAUTH = "org.onap.auth.oauth";
+    private static final String OAUTH_TOKEN_MGR = OAUTH + ".TokenMgr";
+    private static final String OAUTH_HTTP_TAF = OAUTH + ".OAuth2HttpTaf";
+    private static final String OAUTH_DIRECT_TAF = OAUTH + ".OAuthDirectTAF";
+    public static final String UTF_8 = "UTF-8";
+
+    // Property Names associated with configurations.
+    // As of 1.0.2, these have had the dots removed so as to be compatible with
+    // JavaBean style
+    // configurations as well as property list style.
+    public static final String HOSTNAME = "hostname";
+    public static final String CADI_PROP_FILES = "cadi_prop_files"; // Additional Properties files (separate with ;)
+    public static final String CADI_LOGLEVEL = "cadi_loglevel";
+    public static final String CADI_LOGDIR = "cadi_log_dir";
+    public static final String CADI_ETCDIR = "cadi_etc_dir";
+    public static final String CADI_LOGNAME = "cadi_logname";
+    // public static final String CADI_LOGFMT="cad_logging_format";
+    // public static final String CADI_LOGFMT_UTC="UTC";
+    // public static final String CADI_LOGFMT_ISO8601="ISO-8601";
+    public static final String CADI_KEYFILE = "cadi_keyfile";
+    public static final String CADI_KEYSTORE = "cadi_keystore";
+    public static final String CADI_KEYSTORE_PASSWORD = "cadi_keystore_password";
+    public static final String CADI_ALIAS = "cadi_alias";
+    public static final String CADI_CLIENT_ALIAS = "cadi_client_alias";
+    public static final String CADI_LOGINPAGE_URL = "cadi_loginpage_url";
+    public static final String CADI_LATITUDE = "cadi_latitude";
+    public static final String CADI_LONGITUDE = "cadi_longitude";
+
+    public static final String CADI_KEY_PASSWORD = "cadi_key_password";
+    public static final String CADI_TRUSTSTORE = "cadi_truststore";
+    public static final String CADI_TRUSTSTORE_PASSWORD = "cadi_truststore_password";
+    public static final String CADI_X509_ISSUERS = "cadi_x509_issuers";
+    public static final String CADI_TRUST_MASKS = "cadi_trust_masks";
+    public static final String CADI_TRUST_PERM = "cadi_trust_perm"; // IDs with this perm can utilize the "AS " user
+                                                                    // concept
+    public static final String CADI_PROTOCOLS = "cadi_protocols";
+    public static final String CADI_NOAUTHN = "cadi_noauthn";
+    public static final String CADI_LOC_LIST = "cadi_loc_list";
+
+    // Special Behaviors
+    public static final String CADI_BATH_CONVERT = "cadi_bath_convert";
+    public static final String CADI_API_ENFORCEMENT = "cadi_api_enforcement";
+    public static final String CADI_ADD_TAFS = "cadi_add_tafs";
+    public static final String CADI_ADD_LURS = "cadi_add_lurs";
+
+    public static final String CADI_USER_CHAIN_TAG = "cadi_user_chain";
+    public static final String CADI_USER_CHAIN = "USER_CHAIN";
+
+    public static final String CADI_OAUTH2_URL = "cadi_oauth2_url";
+    public static final String CADI_TOKEN_DIR = "cadi_token_dir";
+
+    public static final String HTTPS_PROTOCOLS = "https.protocols";
+    public static final String HTTPS_CLIENT_PROTOCOLS = "jdk.tls.client.protocols";
+    public static final String HTTPS_PROTOCOLS_DEFAULT = "TLSv1.1,TLSv1.2";
+    public static final String HTTPS_CIPHER_SUITES = "https.cipherSuites";
+    public static final String HTTPS_CIPHER_SUITES_DEFAULT = "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,"
+            + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,"
+            + "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_DSS_WITH_AES_128_CBC_SHA,"
+            + "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,TLS_ECDHE_RSA_WITH_RC4_128_SHA,TLS_ECDH_ECDSA_WITH_RC4_128_SHA,"
+            + "TLS_ECDH_RSA_WITH_RC4_128_SHA,TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,"
+            + "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,TLS_EMPTY_RENEGOTIATION_INFO_SCSV";
+
+    public static final String LOCALHOST_ALLOW = "localhost_allow";
+    public static final String LOCALHOST_DENY = "localhost_deny";
+
+    public static final String BASIC_REALM = "basic_realm"; // what is sent to the client
+    public static final String BASIC_WARN = "basic_warn"; // Warning of insecure channel
+    public static final String USERS = "local_users";
+    public static final String GROUPS = "local_groups";
+    public static final String WRITE_TO = "local_writeto"; // dump RBAC to local file in Tomcat Style (some apps use)
+
+    public static final String OAUTH_CLIENT_ID = "client_id";
+    public static final String OAUTH_CLIENT_SECRET = "client_secret";
+
+    public static final String AAF_ENV = "aaf_env";
+    public static final String AAF_ROOT_NS = "aaf_root_ns";
+    public static final String AAF_ROOT_NS_DEF = "org.osaaf.aaf";
+    public static final String AAF_ROOT_COMPANY = "aaf_root_company";
+    /**
+     * Use Config.getAAFLocateUrl(access) to get correct property in/out of
+     * container
+     */
+    public static final String AAF_LOCATE_URL = "aaf_locate_url"; // URL for AAF locator
+    public static final String AAF_LOCATE_URL_TAG = "AAF_LOCATE_URL"; // Name of Above for use in Config Variables.
+    public static final String AAF_DEFAULT_API_VERSION = "2.1";
+    public static final String AAF_DEPLOYED_VERSION = "aaf_deployed_version";
+    public static final String AAF_API_VERSION = "aaf_api_version";
+    public static final String AAF_URL = "aaf_url"; // URL for AAF... Use to trigger AAF configuration
+    public static final String AAF_LOCATOR_CLASS = "aaf_locator_class";
+    // AAF Locator Entries are ADDITIONAL entries, which also gives the Property
+    // ability
+    // to set these entries manually
+    // example: adding a K8S name like "oom"
+    // this will allow Registrations to pick up
+    // locator_ns.oom for onap's "OOM" based k8s entries, etc.
+    public static final String AAF_LOCATOR_CONTAINER = "aaf_locator_container";
+    // An ID for another Container, to be used to avoid picking up the wrong
+    // internal info
+    // for another container.
+    public static final String AAF_LOCATOR_CONTAINER_ID = "aaf_locator_container_id";
+    public static final String AAF_LOCATOR_CONTAINER_NS = "aaf_locator_container_ns";
+    public static final String AAF_LOCATOR_VERSION = "aaf_locator_version";
+    public static final String AAF_LOCATOR_PROTOCOL = "aaf_locator_protocol";
+    public static final String AAF_LOCATOR_SUBPROTOCOL = "aaf_locator_subprotocol";
+    public static final String AAF_LOCATOR_APP_NS = "aaf_locator_app_ns";
+    public static final String AAF_LOCATOR_ENTRIES = "aaf_locator_entries";
+    public static final String AAF_LOCATOR_FQDN = "aaf_locator_fqdn";
+    public static final String AAF_LOCATOR_NAME = "aaf_locator_name";
+    public static final String AAF_LOCATOR_PUBLIC_PORT = "aaf_locator_public_port";
+    public static final String AAF_LOCATOR_PUBLIC_FQDN = "aaf_locator_public_fqdn";
+    public static final String AAF_LOCATOR_PUBLIC_NAME = "aaf_locator_public_name";
+
+    // AAF Service will write to the Audit Log if a past due AAF stored Password
+    // is being used within # of days specified.
+    public static final String AAF_CRED_WARN_DAYS = "aaf_cred_warn_days";
+    public static final String AAF_CRED_WARN_DAYS_DFT = "7";
+
+    public static final String AAF_APPID = "aaf_id";
+    public static final String AAF_APPPASS = "aaf_password";
+    public static final String AAF_LUR_CLASS = "aaf_lur_class";
+    public static final String AAF_TAF_CLASS = "aaf_taf_class";
+    public static final String AAF_CONNECTOR_CLASS = "aaf_connector_class";
+    public static final String AAF_CONN_TIMEOUT = "aaf_conn_timeout";
+    public static final String AAF_CONN_TIMEOUT_DEF = "3000";
+    public static final String AAF_CONN_IDLE_TIMEOUT = "aaf_conn_idle_timeout"; // only for Direct Jetty Access.
+    public static final String AAF_CONN_IDLE_TIMEOUT_DEF = "10000"; // only for Direct Jetty Access.
+
+    // Default Classes: These are for Class loading to avoid direct compile links
+    public static final String AAF_TAF_CLASS_DEF = "org.onap.aaf.cadi.aaf.v2_0.AAFTaf";
+    public static final String AAF_LOCATOR_CLASS_DEF = "org.onap.aaf.cadi.aaf.v2_0.AAFLocator";
+    public static final String AAF_LOCATOR_CLASS_SINGLE = "org.onap.aaf.cadi.locator.SingleEndpointLocator";
+
+    public static final String CADI_OLUR_CLASS_DEF = "org.onap.aaf.cadi.olur.OLur";
+    public static final String CADI_OBASIC_HTTP_TAF_DEF = "org.onap.aaf.cadi.obasic.OBasicHttpTaf";
+    public static final String CADI_AAF_CON_DEF = "org.onap.aaf.cadi.aaf.v2_0.AAFCon";
+
+    public static final String AAF_CALL_TIMEOUT = "aaf_timeout";
+    public static final String AAF_CALL_TIMEOUT_DEF = "5000";
+    public static final String AAF_USER_EXPIRES = "aaf_user_expires";
+    public static final String AAF_USER_EXPIRES_DEF = "600000"; // Default is 10 mins
+    public static final String AAF_CLEAN_INTERVAL = "aaf_clean_interval";
+    public static final String AAF_CLEAN_INTERVAL_DEF = "30000"; // Default is 30 seconds
+    public static final String AAF_REFRESH_TRIGGER_COUNT = "aaf_refresh_trigger_count";
+    public static final String AAF_REFRESH_TRIGGER_COUNT_DEF = "3"; // Default is 10 mins
+
+    public static final String AAF_HIGH_COUNT = "aaf_high_count";
+    public static final String AAF_HIGH_COUNT_DEF = "1000"; // Default is 1000 entries
+    public static final String AAF_PERM_MAP = "aaf_perm_map";
+    // public static final String AAF_COMPONENT = "aaf_component";
+    public static final String AAF_CERT_IDS = "aaf_cert_ids";
+    public static final String AAF_DEBUG_IDS = "aaf_debug_ids"; // comma delimited
+    public static final String AAF_DATA_DIR = "aaf_data_dir"; // AAF processes and Components only.
+
+    public static final String AAF_URL_OAUTH = "aaf_url_oauth";
+    public static final String AAF_URL_GUI = "aaf_url_gui";
+    public static final String AAF_URL_FS = "aaf_url_fs";
+    public static final String AAF_URL_CM = "aaf_url_cm";
+    public static final String AAF_URL_CM_DEF = "https://AAF_LOCATE_URL/AAF_NS.cm:" + AAF_DEFAULT_API_VERSION;
+    public static final String AAF_URL_HELLO = "aaf_url_hello";
+    public static final String CM_TRUSTED_CAS = "cm_trusted_cas";
+    // let NS Owners choose with <ns>.certman aaf ignoreIPs" to ignoreIP Check for
+    // Configs
+    // Probably only want to allow in a DEV Env.
+    public static final String CM_ALLOW_IGNORE_IPS = "cm_allow_ignore_ips";
+    // Docker doesn't have a default DNS. The property turns off IP Checking of DNSs
+    // before creating.
+    public static final String CM_ALWAYS_IGNORE_IPS = "cm_always_ignore_ips";
+
+    public static final String PATHFILTER_URLPATTERN = "pathfilter_urlpattern";
+    public static final String PATHFILTER_STACK = "pathfilter_stack";
+    public static final String PATHFILTER_NS = "pathfilter_ns";
+    public static final String PATHFILTER_NOT_AUTHORIZED_MSG = "pathfilter_not_authorized_msg";
+
+    // This one should go unpublic
+    public static final String AAF_DEFAULT_REALM = "aaf_default_realm";
+    private static String defaultRealm = "people.osaaf.org";
+
+    public static final String AAF_DOMAIN_SUPPORT = "aaf_domain_support";
+    public static final String AAF_DOMAIN_SUPPORT_DEF = ".com:.org";
+
+    // OAUTH2
+    public static final String AAF_OAUTH2_TOKEN_URL = "aaf_oauth2_token_url";
+    public static final String AAF_OAUTH2_INTROSPECT_URL = "aaf_oauth2_introspect_url";
+    public static final String AAF_ALT_OAUTH2_TOKEN_URL = "aaf_alt_oauth2_token_url";
+    public static final String AAF_ALT_OAUTH2_INTROSPECT_URL = "aaf_alt_oauth2_introspect_url";
+    public static final String AAF_ALT_OAUTH2_DOMAIN = "aaf_alt_oauth2_domain";
+    public static final String AAF_ALT_CLIENT_ID = "aaf_alt_oauth2_client_id";
+    public static final String AAF_ALT_CLIENT_SECRET = "aaf_alt_oauth2_client_secret";
+    public static final String AAF_OAUTH2_HELLO_URL = "aaf_oauth2_hello_url";
+    
+    public static void setDefaultRealm(Access access) {
+        try {
+            defaultRealm = logProp(access, Config.AAF_DEFAULT_REALM, logProp(access, Config.BASIC_REALM,
+                    logProp(access, HOSTNAME, InetAddress.getLocalHost().getHostName())));
+        } catch (UnknownHostException e) {
+            access.log(Level.INIT, "Unable to determine Hostname", e);
+        }
+    }
+
+    public static HttpTaf configHttpTaf(Connector con, SecurityInfoC<HttpURLConnection> si, TrustChecker tc, CredVal up,
+            Lur lur, Object... additionalTafLurs) throws CadiException, LocatorException {
+        Access access = si.access;
+        RegistrationPropHolder rph;
+        try {
+            rph = new RegistrationPropHolder(access, 0);
+        } catch (UnknownHostException e2) {
+            throw new CadiException(e2);
+        }
+        /////////////////////////////////////////////////////
+        // Setup AAFCon for any following
+        /////////////////////////////////////////////////////
+        Class<?> aafConClass = loadClass(access, CADI_AAF_CON_DEF);
+        Object aafcon = null;
+        if (con != null && aafConClass != null && aafConClass.isAssignableFrom(con.getClass())) {
+            aafcon = con;
+        } else if (lur != null) {
+            Field f;
+            try {
+                f = lur.getClass().getField("aaf");
+                aafcon = f.get(lur);
+            } catch (Exception e) {
+                access.log(Level.INIT, e);
+            }
+        }
+
+        boolean hasDirectAAF = hasDirect("DirectAAFLur", additionalTafLurs);
+        // IMPORTANT! Don't attempt to load AAF Connector if there is no AAF URL
+        String aafURL = logProp(rph, AAF_URL, null);
+        if (!hasDirectAAF && aafcon == null && aafURL != null) {
+            aafcon = loadAAFConnector(si, aafURL);
+        }
+
+        HttpTaf taf;
+        // Setup Host, in case Network reports an unusable Hostname (i.e. VTiers, VPNs,
+        // etc)
+        String hostname = logProp(access, HOSTNAME, null);
+        if (hostname == null) {
+            try {
+                hostname = InetAddress.getLocalHost().getHostName();
+            } catch (UnknownHostException e1) {
+                throw new CadiException("Unable to determine Hostname", e1);
+            }
+        }
+
+        access.log(Level.INIT, "Hostname set to", hostname);
+        // Get appropriate TAFs
+        ArrayList<Priori<HttpTaf>> htlist = new ArrayList<>();
+
+        /////////////////////////////////////////////////////
+        // Add a Denial of Service TAF
+        // Note: how IPs and IDs are added are up to service type.
+        // They call "DenialOfServiceTaf.denyIP(String) or denyID(String)
+        /////////////////////////////////////////////////////
+        htlist.add(new Priori<HttpTaf>(new DenialOfServiceTaf(access), 0));
+
+        /////////////////////////////////////////////////////
+        // Configure Client Cert TAF
+        /////////////////////////////////////////////////////
+        X509Taf x509TAF = null;
+        String truststore = logProp(access, CADI_TRUSTSTORE, null);
+        if (truststore != null) {
+            String truststorePwd = access.getProperty(CADI_TRUSTSTORE_PASSWORD, null);
+            if (truststorePwd != null) {
+                if (truststorePwd.startsWith(Symm.ENC)) {
+                    try {
+                        access.decrypt(truststorePwd, false);
+                    } catch (IOException e) {
+                        throw new CadiException(CADI_TRUSTSTORE_PASSWORD + " cannot be decrypted", e);
+                    }
+                }
+                try {
+                    x509TAF = new X509Taf(access, lur);
+                    htlist.add(new Priori<HttpTaf>(x509TAF, 10));
+                    access.log(Level.INIT, "Certificate Authorization enabled");
+                } catch (SecurityException | IllegalArgumentException e) {
+                    access.log(Level.INIT,
+                            "AAFListedCertIdentity cannot be instantiated. Certificate Authorization is now disabled",
+                            e);
+                } catch (CertificateException e) {
+                    access.log(Level.INIT, "Certificate Authorization failed, it is disabled", e);
+                } catch (NoSuchAlgorithmException e) {
+                    access.log(Level.INIT, "Certificate Authorization failed, wrong Security Algorithm", e);
+                }
+            }
+        } else {
+            access.log(Level.INIT, "Certificate Authorization not enabled");
+        }
+
+        /////////////////////////////////////////////////////
+        // Configure Basic Auth (local content)
+        /////////////////////////////////////////////////////
+        boolean hasOAuthDirectTAF = hasDirect("DirectOAuthTAF", additionalTafLurs);
+        String basicRealm = logProp(access, BASIC_REALM, null);
+        String aafCleanup = logProp(access, AAF_USER_EXPIRES, AAF_USER_EXPIRES_DEF); // Default is 10 mins
+        long userExp = Long.parseLong(aafCleanup);
+        boolean basicWarn = "TRUE".equals(access.getProperty(BASIC_WARN, "FALSE"));
+
+        if (!hasDirectAAF) {
+            HttpTaf aaftaf = null;
+            if (!hasOAuthDirectTAF) {
+                if (basicRealm != null) {
+                    @SuppressWarnings("unchecked")
+                    Class<HttpTaf> obasicCls = (Class<HttpTaf>) loadClass(access, CADI_OBASIC_HTTP_TAF_DEF);
+                    if (obasicCls != null) {
+                        try {
+                            String tokenurl = logProp(rph, Config.AAF_OAUTH2_TOKEN_URL, null);
+                            String introspecturl = logProp(rph, Config.AAF_OAUTH2_INTROSPECT_URL, null);
+                            if (tokenurl == null || introspecturl == null) {
+                                access.log(Level.INIT,
+                                        "Both tokenurl and introspecturl are required. Oauth Authorization is disabled.");
+                            } else {
+                                // try to construct the TAF instance.  Try without the CredVal first (original code), change
+                                // to try with a CredVal paramater if it fails as the newer ONAP code contains this in the OBasicHttpTaf constructor
+                                System.out.println("TokenURL="+ tokenurl + "; IntrospectURL="+introspecturl);
+                                Constructor<HttpTaf> obasicConst = null;
+                                try {
+                                    obasicConst = obasicCls.getConstructor(PropAccess.class, String.class,
+                                        String.class, String.class);
+                                    htlist.add(new Priori<HttpTaf>(
+                                            obasicConst.newInstance(access, basicRealm, tokenurl, introspecturl), 20));
+                                } catch (Exception e) {
+                                    obasicConst = obasicCls.getConstructor(PropAccess.class, CredVal.class, String.class, String.class, String.class);
+                                    htlist.add(new Priori<HttpTaf>(
+                                        obasicConst.newInstance(access, up, basicRealm, tokenurl, introspecturl), 20));
+                                }
+
+                                access.log(Level.INIT, "Oauth supported Basic Authorization is enabled");
+                            }
+                        } catch (NoSuchMethodException | SecurityException | InstantiationException
+                                | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+                            access.log(Level.INIT, e);
+                        }
+                    } else if (up != null) {
+                        access.log(Level.INIT, "Basic Authorization is enabled using realm", basicRealm);
+                        // Allow warning about insecure channel to be turned off
+                        if (!basicWarn) {
+                            access.log(Level.INIT, "WARNING! The basicWarn property has been set to false.",
+                                    " There will be no additional warning if Basic Auth is used on an insecure channel");
+                        }
+                        BasicHttpTaf bht = new BasicHttpTaf(access, up, basicRealm, userExp, basicWarn);
+                        for (Object o : additionalTafLurs) {
+                            if (o instanceof CredValDomain) {
+                                bht.add((CredValDomain) o);
+                            }
+                        }
+                        if (x509TAF != null) {
+                            x509TAF.add(bht);
+                        }
+                        htlist.add(new Priori<HttpTaf>(bht, 20));
+                        access.log(Level.INIT, "Basic Authorization is enabled");
+                    }
+                } else {
+                    access.log(Level.INIT,
+                            "Local Basic Authorization is disabled.  Enable by setting basicRealm=<appropriate realm, i.e. my.att.com>");
+                }
+
+                /////////////////////////////////////////////////////
+                // Configure AAF Driven Basic Auth
+                /////////////////////////////////////////////////////
+                if (aafcon == null) {
+                    access.log(Level.INIT, "AAF Connection (AAFcon) is null.  Cannot create an AAF TAF");
+                } else if (aafURL == null) {
+                    access.log(Level.INIT, "No AAF URL in properties, Cannot create an AAF TAF");
+                } else {// There's an AAF_URL... try to configure an AAF
+                    String aafTafClassName = logProp(access, AAF_TAF_CLASS, AAF_TAF_CLASS_DEF);
+                    // Only 2.0 available at this time
+                    if (AAF_TAF_CLASS_DEF.equals(aafTafClassName)) {
+                        try {
+                            Class<?> aafTafClass = loadClass(access, aafTafClassName);
+                            if (aafTafClass != null) {
+                                Constructor<?> cstr = aafTafClass.getConstructor(Connector.class, boolean.class,
+                                        AbsUserCache.class);
+                                if (cstr != null) {
+                                    if (lur instanceof AbsUserCache) {
+                                        aaftaf = (HttpTaf) cstr.newInstance(aafcon, basicWarn, lur);
+                                    } else {
+                                        cstr = aafTafClass.getConstructor(Connector.class, boolean.class);
+                                        if (cstr != null) {
+                                            aaftaf = (HttpTaf) cstr.newInstance(aafcon, basicWarn);
+                                        }
+                                    }
+                                    if (aaftaf == null) {
+                                        access.log(Level.INIT, "ERROR! AAF TAF Failed construction.  NOT Configured");
+                                    } else {
+                                        access.log(Level.INIT, "AAF TAF Configured to ", aafURL);
+                                        // Note: will add later, after all others configured
+                                    }
+                                }
+                            } else {
+                                access.log(Level.INIT,
+                                        "There is no AAF TAF class available: %s. AAF TAF not configured.",
+                                        aafTafClassName);
+                            }
+                        } catch (Exception e) {
+                            access.log(Level.INIT, "ERROR! AAF TAF Failed construction.  NOT Configured", e);
+                        }
+                    }
+                }
+            }
+
+            /////////////////////////////////////////////////////
+            // Configure OAuth TAF
+            /////////////////////////////////////////////////////
+            if (!hasOAuthDirectTAF) {
+                String oauthTokenUrl = logProp(rph, Config.AAF_OAUTH2_TOKEN_URL, null);
+                Class<?> oadtClss;
+                try {
+                    oadtClss = Class.forName(OAUTH_DIRECT_TAF);
+                } catch (ClassNotFoundException e1) {
+                    oadtClss = null;
+                    access.log(Level.DEBUG, e1);
+                }
+                if (additionalTafLurs != null && additionalTafLurs.length > 0
+                        && (oadtClss != null && additionalTafLurs[0].getClass().isAssignableFrom(oadtClss))) {
+                    htlist.add(new Priori<HttpTaf>((HttpTaf) additionalTafLurs[0], 30));
+                    String[] array = new String[additionalTafLurs.length - 1];
+                    if (array.length > 0) {
+                        System.arraycopy(htlist, 1, array, 0, array.length);
+                    }
+                    additionalTafLurs = array;
+                    access.log(Level.INIT, "OAuth2 Direct is enabled");
+                } else if (oauthTokenUrl != null) {
+                    String oauthIntrospectUrl = logProp(rph, Config.AAF_OAUTH2_INTROSPECT_URL, null);
+                    @SuppressWarnings("unchecked")
+                    Class<HttpTaf> oaTCls = (Class<HttpTaf>) loadClass(access, OAUTH_HTTP_TAF);
+                    if (oaTCls != null) {
+                        Class<?> oaTTmgrCls = loadClass(access, OAUTH_TOKEN_MGR);
+                        if (oaTTmgrCls != null) {
+                            try {
+                                Method oaTTmgrGI = oaTTmgrCls.getMethod("getInstance", PropAccess.class, String.class,
+                                        String.class);
+                                Object oaTTmgr = oaTTmgrGI.invoke(null /* this is static method */, access,
+                                        oauthTokenUrl, oauthIntrospectUrl);
+                                Constructor<HttpTaf> oaTConst = oaTCls.getConstructor(Access.class, oaTTmgrCls);
+                                htlist.add(new Priori<HttpTaf>(oaTConst.newInstance(access, oaTTmgr), 30));
+                                access.log(Level.INIT, "OAuth2 TAF is enabled");
+                            } catch (NoSuchMethodException | SecurityException | IllegalAccessException
+                                    | IllegalArgumentException | InvocationTargetException | InstantiationException e) {
+                                access.log(Level.INIT, "OAuth2HttpTaf cannot be instantiated. OAuth2 is disabled", e);
+                            }
+                        }
+                    }
+                } else {
+                    access.log(Level.INIT, "OAuth TAF is not configured");
+                }
+            }
+
+            /////////////////////////////////////////////////////
+            // Adding BasicAuth (AAF) last, after other primary Cookie Based
+            // Needs to be before Cert... see below
+            /////////////////////////////////////////////////////
+            if (aaftaf != null) {
+                htlist.add(new Priori<HttpTaf>(aaftaf, 40));
+            }
+        }
+
+        /////////////////////////////////////////////////////
+        // Any Additional Tafs passed in Constructor
+        /////////////////////////////////////////////////////
+        if (additionalTafLurs != null) {
+            int i = 0;
+            for (Object additional : additionalTafLurs) {
+                if (additional instanceof BasicHttpTaf) {
+                    BasicHttpTaf ht = (BasicHttpTaf) additional;
+                    for (Object cv : additionalTafLurs) {
+                        if (cv instanceof CredValDomain) {
+                            ht.add((CredValDomain) cv);
+                            access.printf(Level.INIT, "%s Authentication is enabled", cv);
+                        }
+                    }
+                    htlist.add(new Priori<HttpTaf>(ht, 50 + i++));
+                } else if (additional instanceof HttpTaf) {
+                    HttpTaf ht = (HttpTaf) additional;
+                    htlist.add(new Priori<HttpTaf>(ht, 50 + i++));
+                    access.printf(Level.INIT, "%s Authentication is enabled", additional.getClass().getSimpleName());
+                } else if (hasOAuthDirectTAF) {
+                    Class<?> daupCls;
+                    try {
+                        daupCls = Class.forName("org.onap.aaf.auth.direct.DirectAAFUserPass");
+                    } catch (ClassNotFoundException e) {
+                        daupCls = null;
+                        access.log(Level.INIT, e);
+                    }
+                    if (daupCls != null && additional.getClass().isAssignableFrom(daupCls)) {
+                        htlist.add(new Priori<HttpTaf>(
+                                new BasicHttpTaf(access, (CredVal) additional, basicRealm, userExp, basicWarn),
+                                50 + i++));
+                        access.printf(Level.INIT, "Direct BasicAuth Authentication is enabled",
+                                additional.getClass().getSimpleName());
+                    }
+                }
+            }
+        }
+
+        // Add BasicAuth, if any, to x509Taf
+        if (x509TAF != null) {
+            for (Priori<HttpTaf> ht : htlist) {
+                if (ht.t instanceof BasicHttpTaf) {
+                    x509TAF.add((BasicHttpTaf) ht.t);
+                }
+            }
+        }
+
+        /////////////////////////////////////////////////////
+        // Additional TAFs by Plugin
+        /////////////////////////////////////////////////////
+        Priori.add(access, CADI_ADD_TAFS, htlist);
+
+        /////////////////////////////////////////////////////
+        // Create EpiTaf from configured TAFs
+        /////////////////////////////////////////////////////
+        if (htlist.size() == 1) {
+            // just return the one
+            taf = htlist.get(0).t;
+        } else {
+            Collections.sort(htlist);
+            HttpTaf[] htarray = new HttpTaf[htlist.size()];
+            int i = -1;
+            StringBuilder sb = new StringBuilder("Tafs processed in this order:\n");
+            for (Priori<HttpTaf> pht : htlist) {
+                htarray[++i] = pht.t;
+                sb.append("    ");
+                sb.append(pht.t.getClass().getName());
+                sb.append('(');
+                sb.append(pht.priority);
+                sb.append(")\n");
+            }
+            access.log(Level.INIT, sb);
+
+            Locator<URI> locator = loadLocator(si, aafURL);
+
+            taf = new HttpEpiTaf(access, locator, tc, htarray); // ok to pass locator == null
+            String level = logProp(access, CADI_LOGLEVEL, null);
+            if (level != null) {
+                access.setLogLevel(Level.valueOf(level));
+            }
+        }
+
+        return taf;
+    }
+
+    public static String logProp(RegistrationPropHolder rph, String tag, String def) {
+        String rv = rph.access().getProperty(tag, def);
+        if (rv == null) {
+            rph.access().log(Level.INIT, tag, "is not explicitly set");
+        } else {
+            rv = rph.replacements("Config.logProp", rv, null, null);
+            rph.access().log(Level.INIT, tag, "is set to", rv);
+        }
+        return rv;
+
+    }
+
+    public static String logProp(Access access, String tag, String def) {
+        String rv = access.getProperty(tag, def);
+        if (rv == null) {
+            access.log(Level.INIT, tag, "is not explicitly set");
+        } else {
+            access.log(Level.INIT, tag, "is set to", rv);
+        }
+        return rv;
+    }
+
+    public static Lur configLur(SecurityInfoC<HttpURLConnection> si, Connector con, Object... additionalTafLurs)
+            throws CadiException {
+        Access access = si.access;
+        RegistrationPropHolder rph;
+        try {
+            rph = new RegistrationPropHolder(access, 0);
+        } catch (UnknownHostException e2) {
+            throw new CadiException(e2);
+        }
+
+        List<Priori<Lur>> lurs = new ArrayList<>();
+
+        /////////////////////////////////////////////////////
+        // Configure a Local Property Based RBAC/LUR
+        /////////////////////////////////////////////////////
+        try {
+            String users = access.getProperty(USERS, null);
+            String groups = access.getProperty(GROUPS, null);
+
+            if (groups != null || users != null) {
+                LocalLur ll = new LocalLur(access, users, groups); // note b64==null is ok.. just means no encryption.
+                lurs.add(new Priori<Lur>(ll, 10));
+
+                String writeto = access.getProperty(WRITE_TO, null);
+                if (writeto != null) {
+                    String msg = UsersDump.updateUsers(writeto, ll);
+                    if (msg != null) {
+                        access.log(Level.INIT, "ERROR! Error Updating ", writeto, "with roles and users:", msg);
+                    }
+                }
+            }
+        } catch (IOException e) {
+            throw new CadiException(e);
+        }
+
+        /////////////////////////////////////////////////////
+        // Configure the OAuth Lur (if any)
+        /////////////////////////////////////////////////////
+        String tokenUrl = logProp(rph, AAF_OAUTH2_TOKEN_URL, null);
+        String introspectUrl = logProp(rph, AAF_OAUTH2_INTROSPECT_URL, null);
+        if (tokenUrl != null && introspectUrl != null) {
+            try {
+                Class<?> olurCls = loadClass(access, CADI_OLUR_CLASS_DEF);
+                if (olurCls != null) {
+                    Constructor<?> olurCnst = olurCls.getConstructor(PropAccess.class, String.class, String.class);
+                    Lur olur = (Lur) olurCnst.newInstance(access, tokenUrl, introspectUrl);
+                    lurs.add(new Priori<Lur>(olur, 20));
+                    access.log(Level.INIT, "OAuth2 LUR enabled");
+                } else {
+                    access.log(Level.INIT, "AAF/OAuth LUR plugin is not available.");
+                }
+            } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException
+                    | IllegalArgumentException | InvocationTargetException e) {
+                String msg = e.getMessage();
+                if (msg == null && e.getCause() != null) {
+                    msg = e.getCause().getMessage();
+                }
+                access.log(Level.INIT, "AAF/OAuth LUR is not instantiated.", msg, e);
+            }
+        } else {
+            access.log(Level.INIT, "OAuth2 Lur disabled");
+        }
+
+        if (con != null) { // try to reutilize connector
+            lurs.add(new Priori<Lur>(con.newLur(), 30));
+        } else {
+            /////////////////////////////////////////////////////
+            // Configure the AAF Lur (if any)
+            /////////////////////////////////////////////////////
+            String aafURL = logProp(rph, AAF_URL, null); // Trigger Property
+            String aafEnv = access.getProperty(AAF_ENV, null);
+            if (aafEnv == null && aafURL != null && access instanceof PropAccess) { // set AAF_ENV from AAF_URL
+                int ec = aafURL.indexOf("envContext=");
+                if (ec > 0) {
+                    ec += 11; // length of envContext=
+                    int slash = aafURL.indexOf('/', ec);
+                    if (slash > 0) {
+                        aafEnv = aafURL.substring(ec, slash);
+                        ((PropAccess) access).setProperty(AAF_ENV, aafEnv);
+                        access.printf(Level.INIT, "Setting aafEnv to %s from aaf_url value", aafEnv);
+                    }
+                }
+            }
+
+            // Don't configure AAF if it is using DirectAccess
+            if (!hasDirect("DirectAAFLur", additionalTafLurs)) {
+                if (aafURL == null) {
+                    access.log(Level.INIT, "No AAF LUR properties, AAF will not be loaded");
+                } else {// There's an AAF_URL... try to configure an AAF
+                    String aafLurClassStr = logProp(access, AAF_LUR_CLASS, AAF_V2_0_AAF_LUR_PERM);
+                    //////////// AAF Lur 2.0 /////////////
+                    if (aafLurClassStr != null && aafLurClassStr.startsWith(AAF_V2_0)) {
+                        try {
+                            Object aafcon = loadAAFConnector(si, aafURL);
+                            if (aafcon == null) {
+                                access.log(Level.INIT, "AAF LUR class,", aafLurClassStr,
+                                        "cannot be constructed without valid AAFCon object.");
+                            } else {
+                                Class<?> aafAbsAAFCon = loadClass(access, AAF_V2_0_AAFCON);
+                                if (aafAbsAAFCon != null) {
+                                    Method mNewLur = aafAbsAAFCon.getMethod("newLur");
+                                    Object aaflur = mNewLur.invoke(aafcon);
+
+                                    if (aaflur == null) {
+                                        access.log(Level.INIT, "ERROR! AAF LUR Failed construction.  NOT Configured");
+                                    } else {
+                                        access.log(Level.INIT, "AAF LUR Configured to ", aafURL);
+                                        lurs.add(new Priori<Lur>((Lur) aaflur, 40));
+                                        String debugIDs = logProp(access, Config.AAF_DEBUG_IDS, null);
+                                        if (debugIDs != null && aaflur instanceof CachingLur) {
+                                            ((CachingLur<?>) aaflur).setDebug(debugIDs);
+                                        }
+                                    }
+                                }
+                            }
+                        } catch (Exception e) {
+                            access.log(e, "AAF LUR class,", aafLurClassStr,
+                                    "could not be constructed with given Constructors.");
+                        }
+                    }
+                }
+            }
+        }
+
+        /////////////////////////////////////////////////////
+        // Any Additional passed in Constructor
+        /////////////////////////////////////////////////////
+        if (additionalTafLurs != null) {
+            int i = 0;
+            for (Object additional : additionalTafLurs) {
+                if (additional instanceof Lur) {
+                    lurs.add(new Priori<Lur>((Lur) additional, 50 + i++));
+                    access.log(Level.INIT, additional);
+                }
+            }
+        }
+
+        /////////////////////////////////////////////////////
+        // Additional LURs by Plugin
+        /////////////////////////////////////////////////////
+        Priori.add(access, CADI_ADD_LURS, lurs);
+
+        /////////////////////////////////////////////////////
+        // Return a Lur based on how many there are...
+        /////////////////////////////////////////////////////
+        switch (lurs.size()) {
+            case 0:
+                access.log(Level.INIT, "WARNING! No CADI LURs configured");
+                // Return a NULL Lur that does nothing.
+                return new NullLur();
+            case 1:
+                return lurs.get(0).t; // Only one, just return it, save processing
+            default:
+                // Multiple Lurs, use EpiLUR to handle
+                Collections.sort(lurs);
+                Lur[] la = new Lur[lurs.size()];
+                int i = -1;
+                StringBuilder sb = new StringBuilder("Lurs processed in this order:\n");
+                for (Priori<Lur> pht : lurs) {
+                    la[++i] = pht.t;
+                    sb.append("    ");
+                    sb.append(pht.t.getClass().getName());
+                    sb.append('(');
+                    sb.append(pht.priority);
+                    sb.append(")\n");
+                }
+                access.log(Level.INIT, sb);
+                return new EpiLur(la);
+        }
+    }
+
+    private static boolean hasDirect(String simpleClassName, Object[] additionalTafLurs) {
+        if (additionalTafLurs != null) {
+            for (Object tf : additionalTafLurs) {
+                if (tf.getClass().getSimpleName().equals(simpleClassName)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @SuppressWarnings("unchecked")
+    public static Object loadAAFConnector(SecurityInfoC<?> si, String aafURL) {
+        Access access = si.access;
+        Object aafcon = null;
+        Class<?> aafConClass = null;
+
+        try {
+            if (aafURL != null) {
+                String aafConnector = access.getProperty(AAF_CONNECTOR_CLASS, AAF_V2_0_AAF_CON_HTTP);
+                if (AAF_V2_0_AAF_CON_HTTP.equals(aafConnector)) {
+                    aafConClass = loadClass(access, AAF_V2_0_AAF_CON_HTTP);
+                    if (aafConClass != null) {
+                        for (Constructor<?> c : aafConClass.getConstructors()) {
+                            List<Object> lo = new ArrayList<>();
+                            for (Class<?> pc : c.getParameterTypes()) {
+                                if (pc.equals(Access.class)) {
+                                    lo.add(access);
+                                } else if (pc.equals(Locator.class)) {
+                                    lo.add(loadLocator((SecurityInfoC<HttpURLConnection>) si, aafURL));
+                                }
+                            }
+                            if (c.getParameterTypes().length != lo.size()) {
+                                continue; // back to another Constructor
+                            } else {
+                                aafcon = c.newInstance(lo.toArray());
+                            }
+                            break;
+                        }
+                    }
+                }
+                if (aafcon != null) {
+                    String mechid = logProp(access, Config.AAF_APPID, null);
+                    String pass = access.getProperty(Config.AAF_APPPASS, null);
+                    if (mechid != null && pass != null) {
+                        try {
+                            Method basicAuth = aafConClass.getMethod("basicAuth", String.class, String.class);
+                            basicAuth.invoke(aafcon, mechid, pass);
+                        } catch (NoSuchMethodException nsme) {
+                            access.log(Level.NONE, nsme);
+                            // it's ok, don't use
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            access.log(e, "AAF Connector could not be constructed with given Constructors.");
+        }
+
+        return aafcon;
+    }
+
+    public static Class<?> loadClass(Access access, String className) {
+        Class<?> cls = null;
+        try {
+            cls = access.classLoader().loadClass(className);
+        } catch (ClassNotFoundException cnfe) {
+            access.log(Level.NONE, cnfe);
+            try {
+                cls = access.getClass().getClassLoader().loadClass(className);
+            } catch (ClassNotFoundException cnfe2) {
+                access.log(Level.NONE, cnfe2);
+                // just return null
+            }
+        }
+        return cls;
+    }
+
+    @SuppressWarnings("unchecked")
+    public static Locator<URI> loadLocator(SecurityInfoC<HttpURLConnection> si, final String _url)
+            throws LocatorException {
+        Access access = si.access;
+        Locator<URI> locator = null;
+        if (_url == null) {
+            access.log(Level.INIT, "No URL passed to 'loadLocator'. Disabled");
+        } else {
+            try {
+                Class<?> aalCls = Class.forName("org.onap.aaf.cadi.aaf.v2_0.AbsAAFLocator");
+                Method aalMth = aalCls.getMethod("create", String.class, String.class);
+                int colon = _url.lastIndexOf(':');
+                if (colon >= 0) {
+                    int slash = _url.indexOf('/', colon);
+                    String version;
+                    if (slash < 0) {
+                        version = _url.substring(colon + 1);
+                    } else {
+                        version = _url.substring(colon + 1, slash);
+                    }
+                    slash = _url.lastIndexOf('/', colon);
+                    if (slash >= 0) {
+                        Object aal = aalMth.invoke(null/* static */, _url.substring(slash + 1, colon), version);
+                        return (Locator<URI>) aal;
+                    }
+                }
+            } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalAccessException
+                    | IllegalArgumentException | InvocationTargetException e) {
+                String msg;
+                char quote;
+                if (e.getCause() != null) {
+                    msg = e.getCause().getMessage();
+                    quote = '"';
+                } else {
+                    msg = "-";
+                    quote = ' ';
+                }
+                access.printf(Level.DEBUG, "Configured AbsAAFLocator not found%c%s%cContinuing Locator creation ",
+                        quote, msg, quote);
+            }
+            // String url = _url.replace("/AAF_NS.", "/%C%CID%AAF_NS.");
+            // String root_ns = access.getProperty(Config.AAF_ROOT_NS, null);
+            String url;
+            RegistrationPropHolder rph;
+            try {
+                rph = new RegistrationPropHolder(access, 0);
+                url = rph.replacements("Config.loadLocator", _url, null, null);
+                access.printf(Level.INFO, "loadLocator URL is %s", url);
+            } catch (UnknownHostException | CadiException e1) {
+                throw new LocatorException(e1);
+            }
+
+            /**
+             * Simplify logic - if we have a URL with /locate/ in it, we use the default locator.
+             * If we have an explicitly set locator from configuration, we use that one.
+             * Otherwise we fall back to the SingleEndpointLocator, basically default normal HTTP client behavior.
+             */
+            String aaf_locator_class = null;
+            if (url.contains("/locate/")) {
+                aaf_locator_class = AAF_LOCATOR_CLASS_DEF;
+            } else if (si.access.getProperty(Config.AAF_LOCATOR_CLASS, null) != null) {
+                aaf_locator_class = si.access.getProperty(Config.AAF_LOCATOR_CLASS, null);
+            }
+            if (aaf_locator_class == null) {
+                aaf_locator_class = Config.AAF_LOCATOR_CLASS_SINGLE; 
+            }
+
+            try {
+                Class<?> lcls = loadClass(access,aaf_locator_class);
+                if (lcls==null) {
+                    throw new CadiException("Need to include aaf-cadi-aaf jar for AAFLocator");
+                }
+                // First check for preloaded
+                try {
+                    Method meth = lcls.getMethod("create",Access.class,String.class);
+                    locator = (Locator<URI>)meth.invoke(null,access,url);
+                } catch (Exception e) {
+                    access.log(Level.NONE, "(Not fatal) Cannot load by create(String)", e);
+                }
+                if (locator==null) {
+                    URI locatorURI = new URI(url);
+                    FixURIinfo fui = new FixURIinfo(locatorURI);
+                    Constructor<?> cnst = lcls.getConstructor(SecurityInfoC.class,URI.class);
+                    locator = (Locator<URI>)cnst.newInstance(new Object[] {si,locatorURI});
+                    int port = fui.getPort();
+                    String portS = port<0?"":(":"+port);
+
+                    access.log(Level.INFO, "AAFLocator [" + locator.getClass().getSimpleName() + "] enabled using " + locatorURI.getScheme() +"://"+fui.getHost() + portS);
+                } else {
+                    access.log(Level.INFO, "AAFLocator [" + locator.getClass().getSimpleName() + "] enabled using " + url);
+                }
+            } catch (InvocationTargetException e) {
+                if (e.getTargetException() instanceof LocatorException) {
+                    throw (LocatorException)e.getTargetException();
+                }
+                access.log(Level.INIT,e.getTargetException().getMessage(),"AAFLocator for",url,"could not be created.",e);
+            } catch (Exception e) {
+                access.log(Level.INIT,"AAFLocator for",url,"could not be created.",e);
+            }
+        }
+        return locator;
+    }
+
+    // Set by CSP, or is hostname.
+    public static String getDefaultRealm() {
+        return defaultRealm;
+    }
+
+    public static String getAAFLocateUrl(Access access) {
+        String rv = null;
+        String cont = access.getProperty(AAF_LOCATOR_CONTAINER,null);
+        if(cont!=null) {
+            rv = access.getProperty(AAF_LOCATE_URL + '.' +cont, null);
+        }
+        if(rv==null) {
+            rv = access.getProperty(AAF_LOCATE_URL, null);
+        }
+        return rv;
+    }
+
+    private static class Priori<T> implements Comparable<Priori<T>> {
+        public final T t;
+        public final int priority;
+
+        public Priori(final T t, final int priority) {
+            this.t = t;
+            this.priority = priority;
+        }
+
+        @Override
+        public int compareTo(Priori<T> o) {
+            if(priority==o.priority) {
+                return 0;
+            } else if(priority<o.priority) {
+                return -1;
+            } else {
+                return 1;
+            }
+        }
+        public static<T> void add(Access access, final String tag, List<Priori<T>> list) {
+            String plugins = access.getProperty(tag, null);
+            if(plugins!=null) {
+                access.log(Level.INIT, "Adding TAF Plugins: ", plugins);
+                for(String tafs : Split.splitTrim(';', plugins)) {
+                    String[] pluginArray = Split.splitTrim(',', tafs);
+                    String clssn = null;
+                    int priority = 60;
+                    switch(pluginArray.length) {
+                        case 0:
+                            break;
+                        case 1:
+                            clssn = tafs;
+                            break;
+                        default:
+                            clssn = pluginArray[0];
+                            try {
+                                priority = Integer.parseInt(pluginArray[1]);
+                            } catch (NumberFormatException nfe) {
+                                access.printf(Level.ERROR, "%s format is <classname>,priority[;...]\n",CADI_ADD_TAFS);
+                            }
+                    }
+
+                    if(clssn!=null) {
+                        Class<?> cls = loadClass(access, clssn);
+                        if(cls!=null) {
+                            try {
+                                @SuppressWarnings("unchecked")
+                                Constructor<T> cnst = (Constructor<T>)cls.getConstructor(Access.class);
+                                try {
+                                    list.add(new Priori<T>(cnst.newInstance(access),priority));
+                                } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+                                    String hostname = access.getProperty(Config.HOSTNAME,null);
+                                    if(hostname==null) {
+                                        access.printf(Level.ERROR, "%s cannot be constructed on this machine.  Set valid 'hostname' in your properties\n",clssn);
+                                    } else {
+                                        access.printf(Level.ERROR, "%s cannot be constructed on %s with Access.\n",clssn, hostname);
+                                    }
+                                }
+                            } catch (NoSuchMethodException | SecurityException e) {
+                                access.printf(Level.ERROR, "%s needs a Constructor taking Access as sole param.\n",clssn);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+   }
+}
+
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/Get.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/Get.java
new file mode 100644 (file)
index 0000000..4bceea2
--- /dev/null
@@ -0,0 +1,97 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk.apps
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.config;
+
+import java.lang.reflect.Method;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.Access.Level;
+
+public interface Get {
+    public String get(String name, String def, boolean print);
+
+
+    /**
+     * A class for Getting info out of "JavaBean" format
+     * @author Jonathan
+     *
+     */
+    public static class Bean implements Get {
+        private Object bean;
+        private Class<?> bc;
+        private Class<?>[] params;
+        private Object[] args;
+
+        public Bean(Object bean) {
+            this.bean = bean;
+            bc = bean.getClass();
+            params = new Class<?>[0]; // note, this will allow to go out of scope after config
+            args = new Object[0];
+        }
+
+        public String get(String name, String def, boolean print) {
+            String str = null;
+            String gname = "get"+Character.toUpperCase(name.charAt(0))+name.substring(1);
+            try {
+                Method meth = bc.getMethod(gname, params);
+                Object obj = meth.invoke(bean, args);
+                str = obj==null?null:obj.toString(); // easy string convert...
+            } catch (Exception e) {
+            }
+
+            // Take def if nothing else
+            if (str==null) {
+                str = def;
+                // don't log defaults
+            } else {
+                str = str.trim(); // this is vital in Property File based values, as spaces can hide easily
+            }
+            // Note: Can't log during configuration
+            return str;
+        }
+    }
+
+    public static Get NULL = new Get() {
+        public String get(String name, String def, boolean print) {
+            return def;
+        }
+    };
+
+    public static class AccessGet implements Get {
+        private Access access;
+        public AccessGet(Access access) {
+            this.access = access;
+        }
+        public String get(String name, String def, boolean print) {
+            String gotten = access.getProperty(name, def);
+            if (print) {
+                if (gotten == null) {
+                    access.log(Level.INIT,name, "is not set");
+                } else {
+                    access.log(Level.INIT,name, "is set to", gotten);
+                }
+            }
+            return gotten;
+        }
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/GetAccess.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/GetAccess.java
new file mode 100644 (file)
index 0000000..ed3faa7
--- /dev/null
@@ -0,0 +1,57 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.config;
+
+import org.onap.ccsdk.apps.cadi.PropAccess;
+
+public class GetAccess extends PropAccess {
+    private final Get getter;
+
+    public GetAccess(Get getter) {
+        super(new String[]{"cadi_prop_files="+getter.get("cadi_prop_files", null, true)});
+        this.getter = getter;
+    }
+
+    /* (non-Javadoc)
+     * @see org.onap.ccsdk.apps.cadi.PropAccess#getProperty(java.lang.String, java.lang.String)
+     */
+    @Override
+    public String getProperty(String tag, String def) {
+        String rv;
+        rv = super.getProperty(tag, null);
+        if (rv==null && getter!=null) {
+            rv = getter.get(tag, null, true);
+        }
+        return rv==null?def:rv;
+    }
+    /* (non-Javadoc)
+     * @see org.onap.ccsdk.apps.cadi.PropAccess#getProperty(java.lang.String)
+     */
+    @Override
+    public String getProperty(String tag) {
+        return getProperty(tag, null);
+    }
+
+    public Get get() {
+        return getter;
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/MultiGet.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/MultiGet.java
new file mode 100644 (file)
index 0000000..f32484b
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.config;
+
+public class MultiGet implements Get {
+    private Get[] getters;
+
+    public MultiGet(Get ... getters) {
+        this.getters = getters;
+    }
+
+    @Override
+    public String get(String name, String def, boolean print) {
+        String str;
+        for (Get getter : getters) {
+            str = getter.get(name, null, print);
+            if (str!=null)
+                return str;
+        }
+        return def;
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/RegistrationPropHolder.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/RegistrationPropHolder.java
new file mode 100644 (file)
index 0000000..17d0279
--- /dev/null
@@ -0,0 +1,312 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ */
+
+package org.onap.ccsdk.apps.cadi.config;
+
+import java.net.Inet4Address;
+import java.net.UnknownHostException;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.Access.Level;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.util.Split;
+
+public class RegistrationPropHolder {
+    private final String PUBLIC_NAME="%NS.%N";
+    private final String REGI="RegistrationProperty: %s='%s'";
+    private final Access access;
+    public String hostname;
+    private int port;
+    public String public_fqdn;
+    private Integer public_port;
+    public Float latitude;
+    public Float longitude;
+    public final String default_fqdn;
+    public final String default_container_ns;
+    public final String default_name;
+    public final String lentries;
+    public final String lcontainer;
+    public final String default_container;
+    private static boolean firstlog = true;
+
+    public RegistrationPropHolder(final Access access, final int port) throws UnknownHostException, CadiException {
+        this.access = access;
+        StringBuilder errs = new StringBuilder();
+        String str;
+        this.port = port;
+
+        lentries=access.getProperty(Config.AAF_LOCATOR_ENTRIES,"");
+        default_container = access.getProperty(Config.AAF_LOCATOR_CONTAINER, "");
+        if(firstlog) {
+            access.printf(Level.INIT, REGI,"default_container",default_container);
+        }
+        if(!default_container.isEmpty()) {
+            lcontainer=',' + default_container; // "" makes a blank default Public Entry
+            str = access.getProperty(Config.AAF_LOCATOR_PUBLIC_PORT+'.'+default_container, null);
+            if(str==null) {
+                str = access.getProperty(Config.AAF_LOCATOR_PUBLIC_PORT, null);
+            }
+        } else {
+            lcontainer=default_container;
+            str = access.getProperty(Config.AAF_LOCATOR_PUBLIC_PORT, null);
+        }
+        if(str!=null) {
+            public_port=Integer.decode(str);
+        }
+        if(firstlog) {
+            access.printf(Level.INIT, "RegistrationProperty: public_port='%d'",public_port);
+        }
+
+        hostname = access.getProperty(Config.HOSTNAME, null);
+        if (hostname==null) {
+            hostname = Inet4Address.getLocalHost().getHostName();
+        }
+        if (hostname==null) {
+            mustBeDefined(errs,Config.HOSTNAME);
+        }
+        if(firstlog) {
+            access.printf(Level.INIT, REGI,"hostname",hostname);
+        }
+
+        public_fqdn = access.getProperty(Config.AAF_LOCATOR_PUBLIC_FQDN, hostname);
+        if(firstlog) {
+            access.printf(Level.INIT, REGI,"public_fqdn",public_fqdn);
+        }
+
+        // Allow Container to reset the standard name for public
+        String container_public_name = access.getProperty(Config.AAF_LOCATOR_PUBLIC_NAME+'.'+default_container, null);
+        if(container_public_name==null) {
+            container_public_name = access.getProperty(Config.AAF_LOCATOR_PUBLIC_NAME, null);
+            if(container_public_name==null) {
+                container_public_name = access.getProperty(Config.AAF_LOCATOR_NAME, PUBLIC_NAME);
+            }
+        }
+        default_name = container_public_name;
+
+        if(firstlog) {
+            access.printf(Level.INIT, REGI,"default_name",default_name);
+        }
+
+        latitude=null;
+        String slatitude = access.getProperty(Config.CADI_LATITUDE, null);
+        if(slatitude == null) {
+            mustBeDefined(errs,Config.CADI_LATITUDE);
+        } else {
+            latitude = Float.parseFloat(slatitude);
+        }
+        if(firstlog) {
+            access.printf(Level.INIT, REGI,"latitude",slatitude);
+        }
+
+        longitude=null;
+        String slongitude = access.getProperty(Config.CADI_LONGITUDE, null);
+        if(slongitude == null) {
+            mustBeDefined(errs,Config.CADI_LONGITUDE);
+        } else {
+            longitude = Float.parseFloat(slongitude);
+        }
+        if(firstlog) {
+            access.printf(Level.INIT, REGI,"longitude",slongitude);
+        }
+
+        String dot_le;
+        // Note: only one of the ports can be public...  Therefore, only the last
+        for(String le : Split.splitTrim(',', lcontainer)) {
+            dot_le = le.isEmpty()?le :"."+le;
+            str = access.getProperty(Config.AAF_LOCATOR_PUBLIC_FQDN+dot_le,null);
+            if( str != null && !str.isEmpty()) {
+                public_fqdn=str;
+                if(firstlog) {
+                    access.printf(Level.INIT, "RegistrationProperty: public_hostname(overloaded by %s)='%s'",dot_le,public_fqdn);
+                }
+            }
+        }
+
+        default_fqdn = access.getProperty(Config.AAF_LOCATOR_FQDN, hostname);
+        if(firstlog) {
+            access.printf(Level.INIT, REGI,"default_fqdn",default_fqdn);
+        }
+        default_container_ns = access.getProperty(Config.AAF_LOCATOR_CONTAINER_NS,"");
+        if(firstlog) {
+            access.printf(Level.INIT, REGI,"default_container_ns",default_container_ns);
+        }
+        if(errs.length()>0) {
+            throw new CadiException(errs.toString());
+        }
+        firstlog = false;
+    }
+
+    private void mustBeDefined(StringBuilder errs, String propname) {
+        errs.append('\n');
+        errs.append(propname);
+        errs.append(" must be defined.");
+
+    }
+
+    public String getEntryFQDN(final String entry, final String dot_le) {
+        String str;
+        if(public_fqdn!=null && dot_le.isEmpty()) {
+            str = public_fqdn;
+        } else {
+            str = access.getProperty(Config.AAF_LOCATOR_FQDN+dot_le, default_fqdn);
+        }
+        return replacements("RegistrationPropHolder.getEntryFQDN",str,entry,dot_le);
+    }
+
+    public String getEntryName(final String entry, final String dot_le) {
+        String str;
+        if(dot_le.isEmpty()) {
+            str = default_name;
+        } else {
+            str = access.getProperty(Config.AAF_LOCATOR_NAME+dot_le, default_name);
+        }
+        return replacements("RegistrationPropHolder.getEntryName",str,entry,dot_le);
+    }
+
+    public String getPublicEntryName(final String entry, final String dot_le) {
+        String str = access.getProperty(Config.AAF_LOCATOR_PUBLIC_NAME+dot_le, null);
+        if(str==null) {
+            str = access.getProperty(Config.AAF_LOCATOR_PUBLIC_NAME,null);
+        }
+        if(str==null) {
+            str = default_name;
+        }
+        return replacements("RegistrationPropHolder.getEntryName",str,entry,dot_le);
+    }
+
+
+    private String getNS(String dot_le) {
+        String ns;
+        ns = access.getProperty(Config.AAF_LOCATOR_APP_NS+dot_le,null);
+        if(ns==null) {
+            ns = access.getProperty(Config.AAF_LOCATOR_APP_NS, "AAF_NS");
+        }
+        return ns;
+    }
+
+
+    public String replacements(final String fromCode, final String source, final String name, final String _dot_le) {
+        if(source == null) {
+            return "";
+        } else if(source.isEmpty()) {
+            return source;
+        }
+        String value = source;
+        String dot_le;
+        if(_dot_le==null) {
+            dot_le = default_container.isEmpty()?"":'.'+default_container;
+        } else {
+            dot_le = _dot_le;
+        }
+
+        String aaf_locator_host = access.getProperty(Config.AAF_LOCATE_URL+dot_le,null);
+        if(aaf_locator_host==null) {
+            aaf_locator_host = access.getProperty(Config.AAF_LOCATE_URL,null);
+        }
+
+        String str;
+        if(aaf_locator_host!=null) {
+            if("https://AAF_LOCATE_URL".equals(value)) {
+                value = aaf_locator_host;
+            } else {
+                str = aaf_locator_host;
+                if(value.indexOf(Config.AAF_LOCATE_URL_TAG)>=0) {
+                    if(!str.endsWith("/")) {
+                        str+='/';
+                    }
+                    if(!str.endsWith("/locate/")) {
+                        str+="locate/";
+                    }
+                    if(value.startsWith("http:")) {
+                        value = value.replace("http://AAF_LOCATE_URL/", str);
+                    } else {
+                        value = value.replace("https://AAF_LOCATE_URL/", str);
+
+                    }
+                }
+            }
+        }
+
+        int atC = value.indexOf("%C");
+        if(atC>=0) {
+            // aaf_locator_container_ns
+            str = access.getProperty(Config.AAF_LOCATOR_CONTAINER_NS+dot_le, default_container_ns);
+            if(str.isEmpty()) {
+                value = value.replace("%CNS"+'.', str);
+            }
+            value = value.replace("%CNS", str);
+
+            str = access.getProperty(Config.AAF_LOCATOR_CONTAINER+dot_le,default_container);
+            if(str.isEmpty()) {
+                value = value.replace("%C"+'.', str);
+            }
+            value = value.replace("%C", str);
+        }
+
+        if(value.indexOf("%NS")>=0) {
+            str = getNS(dot_le);
+            if(str==null || str.isEmpty()) {
+                value = value.replace("%NS"+'.', "");
+            } else {
+                value = value.replace("%NS", str);
+            }
+        }
+
+        // aaf_root_ns
+        if(value.indexOf("AAF_NS")>=0) {
+            str = access.getProperty(Config.AAF_ROOT_NS, Config.AAF_ROOT_NS_DEF) + '.';
+            String temp = value.replace("%AAF_NS.", str);
+            if(temp.equals(value)) { // intended
+                value = value.replace("AAF_NS.", str); // Backward Compatibility
+            } else {
+                value = temp;
+            }
+        }
+
+
+        if(value.indexOf('%')>=0) {
+            // These shouldn't be expected to have dot elements
+            if(name!=null) {
+              value = value.replace("%N", name);
+            }
+            if(default_fqdn!=null) {
+              value = value.replace("%DF", default_fqdn);
+            }
+            if(public_fqdn!=null) {
+              value = value.replace("%PH", public_fqdn);
+            }
+        }
+        access.printf(Level.DEBUG,
+                "RegistrationReplacement from %s, source: %s, dot_le: %s, value: %s",
+                fromCode,source,dot_le,value);
+
+        return value;
+    }
+
+    public int getEntryPort(final String dot_le) {
+        return public_port!=null && dot_le.isEmpty()?
+                public_port:
+                port;
+    }
+
+    public Access access() {
+        return access;
+    }
+}
\ No newline at end of file
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/SecurityInfo.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/SecurityInfo.java
new file mode 100644 (file)
index 0000000..ad0c4c9
--- /dev/null
@@ -0,0 +1,370 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.config;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.rmi.AccessException;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.Access.Level;
+import org.onap.ccsdk.apps.cadi.util.MaskFormatException;
+import org.onap.ccsdk.apps.cadi.util.NetMask;
+import org.onap.ccsdk.apps.cadi.util.Split;
+
+public class SecurityInfo {
+    private static final String SECURITY_ALGO = "RSA";
+    private static final String HTTPS_PROTOCOLS = "https.protocols";
+    private static final String JDK_TLS_CLIENT_PROTOCOLS = "jdk.tls.client.protocols";
+    private static final String INITIALIZING_ERR_FMT = "Error initializing %s: %s";
+    private static final String LOADED_FROM_CADI_PROPERTIES = "%s loaded from CADI Properties";
+    private static final String LOADED_FROM_SYSTEM_PROPERTIES = "%s loaded from System Properties";
+
+    public static final String SSL_KEY_MANAGER_FACTORY_ALGORITHM;
+
+    private SSLSocketFactory socketFactory;
+    private X509KeyManager[] x509KeyManager;
+    private X509TrustManager[] x509TrustManager;
+    public final String defaultAlias;
+    public final String defaultClientAlias;
+    private NetMask[] trustMasks;
+    private SSLContext context;
+    private HostnameVerifier maskHV;
+    public final Access access;
+
+    // Change Key Algorithms for IBM's VM.  Could put in others, if needed.
+    static {
+        if ("IBM Corporation".equalsIgnoreCase(System.getProperty("java.vm.vendor"))) {
+            SSL_KEY_MANAGER_FACTORY_ALGORITHM = "IbmX509";
+        } else {
+            SSL_KEY_MANAGER_FACTORY_ALGORITHM = "SunX509";
+        }
+    }
+
+
+    public SecurityInfo(final Access access) throws CadiException {
+        String msgHelp = "";
+        try {
+            this.access = access;
+            // reuse DME2 Properties for convenience if specific Properties don't exist
+
+            String str = access.getProperty(Config.CADI_ALIAS, null);
+            if(str==null || str.isEmpty()) {
+                defaultAlias = null;
+            } else {
+                defaultAlias = str;
+            }
+
+            str = access.getProperty(Config.CADI_CLIENT_ALIAS, null);
+            if(str==null) {
+                defaultClientAlias = defaultAlias;
+            } else if(str.isEmpty()) {
+                // intentionally off, i.e. cadi_client_alias=
+                defaultClientAlias = null;
+            } else {
+                defaultClientAlias = str;
+            }
+
+            msgHelp = String.format(INITIALIZING_ERR_FMT,"Keystore", access.getProperty(Config.CADI_KEYSTORE, ""));
+            initializeKeyManager();
+
+            msgHelp = String.format(INITIALIZING_ERR_FMT,"Truststore", access.getProperty(Config.CADI_TRUSTSTORE, ""));
+            initializeTrustManager();
+
+
+            msgHelp = String.format(INITIALIZING_ERR_FMT,"Trustmasks", access.getProperty(Config.CADI_TRUST_MASKS, ""));
+            initializeTrustMasks();
+
+            msgHelp = String.format(INITIALIZING_ERR_FMT,"HTTP Protocols", "access properties");
+            setHTTPProtocols(access);
+
+            msgHelp = String.format(INITIALIZING_ERR_FMT,"Context", "TLS");
+            context = SSLContext.getInstance("TLS");
+            context.init(x509KeyManager, x509TrustManager, null);
+            SSLContext.setDefault(context);
+            socketFactory = context.getSocketFactory();
+        } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException | CertificateException | UnrecoverableKeyException | IOException e) {
+            throw new CadiException(msgHelp,e);
+        }
+    }
+
+    public static void setHTTPProtocols(Access access) {
+        String httpsProtocols = System.getProperty(Config.HTTPS_PROTOCOLS);
+        if(httpsProtocols!=null) {
+            access.printf(Level.INIT, LOADED_FROM_SYSTEM_PROPERTIES, HTTPS_PROTOCOLS);
+        } else {
+            httpsProtocols = access.getProperty(Config.HTTPS_PROTOCOLS,null);
+            if(httpsProtocols!=null) {
+                access.printf(Level.INIT, LOADED_FROM_CADI_PROPERTIES, HTTPS_PROTOCOLS);
+            } else {
+                httpsProtocols = access.getProperty(HTTPS_PROTOCOLS, Config.HTTPS_PROTOCOLS_DEFAULT);
+                access.printf(Level.INIT, "%s set by %s in CADI Properties",Config.HTTPS_PROTOCOLS,Config.CADI_PROTOCOLS);
+            }
+            // This needs to be set when people do  not.
+            System.setProperty(HTTPS_PROTOCOLS, httpsProtocols);
+        }
+        String httpsClientProtocols = System.getProperty(JDK_TLS_CLIENT_PROTOCOLS,null);
+        if(httpsClientProtocols!=null) {
+            access.printf(Level.INIT, LOADED_FROM_SYSTEM_PROPERTIES, JDK_TLS_CLIENT_PROTOCOLS);
+        } else {
+            httpsClientProtocols = access.getProperty(Config.HTTPS_CLIENT_PROTOCOLS, null);
+            if(httpsClientProtocols!=null) {
+                access.printf(Level.INIT, LOADED_FROM_CADI_PROPERTIES, Config.HTTPS_CLIENT_PROTOCOLS);
+            } else {
+                httpsClientProtocols = Config.HTTPS_PROTOCOLS_DEFAULT;
+                access.printf(Level.INIT, "%s set from %s",Config.HTTPS_CLIENT_PROTOCOLS, "Default Protocols");
+            }
+            System.setProperty(JDK_TLS_CLIENT_PROTOCOLS, httpsClientProtocols);
+        }
+    }
+
+    /**
+     * @return the scf
+     */
+    public SSLSocketFactory getSSLSocketFactory() {
+        return socketFactory;
+    }
+
+    public SSLContext getSSLContext() {
+        return context;
+    }
+
+    /**
+     * @return the km
+     */
+    public X509KeyManager[] getKeyManagers() {
+        return x509KeyManager;
+    }
+
+    public void checkClientTrusted(X509Certificate[] certarr) throws CertificateException {
+        for (X509TrustManager xtm : x509TrustManager) {
+            xtm.checkClientTrusted(certarr, SECURITY_ALGO);
+        }
+    }
+
+    public void checkServerTrusted(X509Certificate[] certarr) throws CertificateException {
+        for (X509TrustManager xtm : x509TrustManager) {
+            xtm.checkServerTrusted(certarr, SECURITY_ALGO);
+        }
+    }
+
+    public void setSocketFactoryOn(HttpsURLConnection hsuc) {
+        hsuc.setSSLSocketFactory(socketFactory);
+        if (maskHV != null && !maskHV.equals(hsuc.getHostnameVerifier())) {
+            hsuc.setHostnameVerifier(maskHV);
+        }
+    }
+
+    protected void initializeKeyManager() throws CadiException, IOException, NoSuchAlgorithmException, KeyStoreException, CertificateException, UnrecoverableKeyException {
+        String keyStore = access.getProperty(Config.CADI_KEYSTORE, null);
+        if(keyStore==null) {
+            return;
+        } else if (!new File(keyStore).exists()) {
+            throw new CadiException(keyStore + " does not exist");
+        }
+
+        String keyStorePasswd = access.getProperty(Config.CADI_KEYSTORE_PASSWORD, null);
+        keyStorePasswd = (keyStorePasswd == null) ? null : access.decrypt(keyStorePasswd, false);
+        if (keyStore == null || keyStorePasswd == null) {
+            x509KeyManager = new X509KeyManager[0];
+            return;
+        }
+
+        String keyPasswd = access.getProperty(Config.CADI_KEY_PASSWORD, null);
+        keyPasswd = (keyPasswd == null) ? keyStorePasswd : access.decrypt(keyPasswd, false);
+
+        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(SSL_KEY_MANAGER_FACTORY_ALGORITHM);
+
+        ArrayList<X509KeyManager> keyManagers = new ArrayList<>();
+        File file;
+        for (String ksname : Split.splitTrim(',', keyStore)) {
+            String keystoreFormat;
+            if (ksname.endsWith(".p12") || ksname.endsWith(".pkcs12")) {
+                keystoreFormat = "PKCS12";
+            } else {
+                keystoreFormat = "JKS";
+            }
+
+            file = new File(ksname);
+            if (file.exists()) {
+                FileInputStream fis = new FileInputStream(file);
+                try {
+                    KeyStore ks = KeyStore.getInstance(keystoreFormat);
+                    ks.load(fis, keyStorePasswd.toCharArray());
+                    keyManagerFactory.init(ks, keyPasswd.toCharArray());
+                } finally {
+                    fis.close();
+                }
+            }
+        }
+
+        StringBuilder sb = null;
+        for (KeyManager keyManager : keyManagerFactory.getKeyManagers()) {
+            if (keyManager instanceof X509KeyManager) {
+                X509KeyManager xkm = (X509KeyManager)keyManager;
+                keyManagers.add(xkm);
+                if(defaultAlias!=null) {
+                    sb=new StringBuilder("X509 Chain\n");
+                    x509Info(sb,xkm.getCertificateChain(defaultAlias));
+                }
+                if(defaultClientAlias!=null && !defaultClientAlias.equals(defaultAlias)) {
+                    if(sb==null) {
+                        sb = new StringBuilder();
+                    } else {
+                        sb.append('\n');
+                    }
+                    sb.append("X509 Client Chain\n");
+                    x509Info(sb,xkm.getCertificateChain(defaultAlias));
+                }
+            }
+        }
+        x509KeyManager = new X509KeyManager[keyManagers.size()];
+        keyManagers.toArray(x509KeyManager);
+
+        if(sb!=null) {
+            access.log(Level.INIT, sb);
+        }
+    }
+
+    private void x509Info(StringBuilder sb, X509Certificate[] chain) {
+        if(chain!=null) {
+            int i=0;
+            for(X509Certificate x : chain) {
+                sb.append("  ");
+                sb.append(i++);
+                sb.append(')');
+                sb.append("\n    Subject: ");
+                sb.append(x.getSubjectDN());
+                sb.append("\n    Issuer : ");
+                sb.append(x.getIssuerDN());
+                sb.append("\n    Expires: ");
+                sb.append(x.getNotAfter());
+                sb.append('\n');
+            }
+        }
+    }
+
+    protected void initializeTrustManager() throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException, CadiException {
+        String trustStore = access.getProperty(Config.CADI_TRUSTSTORE, null);
+        if(trustStore==null) {
+            return;
+        } else if(!new File(trustStore).exists()) {
+            throw new CadiException(trustStore + " does not exist");
+        }
+
+        String trustStorePasswd = access.getProperty(Config.CADI_TRUSTSTORE_PASSWORD, null);
+        trustStorePasswd = (trustStorePasswd == null ) ? "changeit"/*defacto Java Trust Pass*/ : access.decrypt(trustStorePasswd, false);
+
+        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(SSL_KEY_MANAGER_FACTORY_ALGORITHM);
+        File file;
+        for (String trustStoreName : Split.splitTrim(',',trustStore)) {
+            file = new File(trustStoreName);
+            if (file.exists()) {
+                FileInputStream fis = new FileInputStream(file);
+                try {
+                    KeyStore ts = KeyStore.getInstance("JKS");
+                    ts.load(fis, trustStorePasswd.toCharArray());
+                    trustManagerFactory.init(ts);
+                } finally {
+                    fis.close();
+                }
+            }
+        }
+
+        TrustManager trustManagers[] = trustManagerFactory.getTrustManagers();
+        if (trustManagers == null || trustManagers.length == 0) {
+            return;
+        }
+
+        x509TrustManager = new X509TrustManager[trustManagers.length];
+        for (int i = 0; i < trustManagers.length; ++i) {
+            try {
+                x509TrustManager[i] = (X509TrustManager)trustManagers[i];
+            } catch (ClassCastException e) {
+                access.log(Level.WARN, "Non X509 TrustManager", x509TrustManager[i].getClass().getName(), "skipped in SecurityInfo");
+            }
+        }
+    }
+
+    protected void initializeTrustMasks() throws AccessException {
+        String tips = access.getProperty(Config.CADI_TRUST_MASKS, null);
+        if (tips == null) {
+            return;
+        }
+
+        access.log(Level.INIT, "Explicitly accepting valid X509s from", tips);
+        String[] ipsplit = Split.splitTrim(',', tips);
+        trustMasks = new NetMask[ipsplit.length];
+        for (int i = 0; i < ipsplit.length; ++i) {
+            try {
+                trustMasks[i] = new NetMask(ipsplit[i]);
+            } catch (MaskFormatException e) {
+                throw new AccessException("Invalid IP Mask in " + Config.CADI_TRUST_MASKS, e);
+            }
+        }
+
+        final HostnameVerifier origHV = HttpsURLConnection.getDefaultHostnameVerifier();
+        maskHV = new HostnameVerifier() {
+            @Override
+            public boolean verify(final String urlHostName, final SSLSession session) {
+                try {
+                    // This will pick up /etc/host entries as well as DNS
+                    InetAddress ia = InetAddress.getByName(session.getPeerHost());
+                    for (NetMask tmask : trustMasks) {
+                        if (tmask.isInNet(ia.getHostAddress())) {
+                            return true;
+                        }
+                    }
+                } catch (UnknownHostException e) {
+                    // It's ok. do normal Verify
+                }
+                return origHV.verify(urlHostName, session);
+            };
+        };
+        HttpsURLConnection.setDefaultHostnameVerifier(maskHV);
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/SecurityInfoC.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/SecurityInfoC.java
new file mode 100644 (file)
index 0000000..1d8ced5
--- /dev/null
@@ -0,0 +1,94 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.config;
+
+import java.net.HttpURLConnection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.SecuritySetter;
+
+
+public class SecurityInfoC<CLIENT> extends SecurityInfo {
+    public static final String DEF_ID = "ID not Set";
+    private static Map<Class<?>,SecurityInfoC<?>> sicMap = new HashMap<>();
+    public SecuritySetter<CLIENT> defSS;
+
+
+    public SecurityInfoC(Access access) throws CadiException {
+        super(access);
+        defSS = new DEFSS<CLIENT>();
+    }
+
+    @SuppressWarnings("unchecked")
+    public static synchronized <CLIENT> SecurityInfoC<CLIENT> instance(Access access, Class<CLIENT> cls) throws CadiException {
+        SecurityInfoInit<CLIENT> sii;
+        if (cls.isAssignableFrom(HttpURLConnection.class)) {
+            try {
+                @SuppressWarnings("rawtypes")
+                Class<SecurityInfoInit> initCls = (Class<SecurityInfoInit>)Class.forName("org.onap.ccsdk.apps.cadi.http.HSecurityInfoInit");
+                sii = initCls.newInstance();
+            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
+                throw new CadiException("CADI using HttpURLConnection requires cadi-client jar",e);
+            }
+        } else {
+            sii = new SecurityInfoInit<CLIENT>() {
+                @Override
+                public SecuritySetter<CLIENT> bestDefault(SecurityInfoC<CLIENT> si) throws CadiException {
+                    return new DEFSS<CLIENT>();
+                }
+            };
+        }
+
+        SecurityInfoC<CLIENT> sic = (SecurityInfoC<CLIENT>) sicMap.get(cls);
+        if (sic==null) {
+            sic = new SecurityInfoC<CLIENT>(access);
+            sic.set(sii.bestDefault(sic));
+            sicMap.put(cls, sic);
+        }
+        return sic;
+    }
+
+    public SecurityInfoC<CLIENT> set(SecuritySetter<CLIENT> defSS) {
+        this.defSS = defSS;
+        return this;
+    }
+
+    private static class DEFSS<C> implements SecuritySetter<C> {
+        @Override
+        public String getID() {
+            return DEF_ID;
+        }
+
+        @Override
+        public void setSecurity(C client) throws CadiException {
+            throw new CadiException("No Client Credentials set.");
+        }
+
+        @Override
+        public int setLastResponse(int respCode) {
+            return 0;
+        }
+    };
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/SecurityInfoInit.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/SecurityInfoInit.java
new file mode 100644 (file)
index 0000000..88229b8
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+package org.onap.ccsdk.apps.cadi.config;
+
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.SecuritySetter;
+
+public interface SecurityInfoInit<CLIENT> {
+    public SecuritySetter<CLIENT> bestDefault(SecurityInfoC<CLIENT> si) throws CadiException;
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/UsersDump.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/config/UsersDump.java
new file mode 100644 (file)
index 0000000..6fc83f5
--- /dev/null
@@ -0,0 +1,162 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.config;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.Date;
+import java.util.HashSet;
+
+import org.onap.ccsdk.apps.cadi.AbsUserCache;
+import org.onap.ccsdk.apps.cadi.lur.LocalLur;
+
+public class UsersDump {
+
+    /**
+     * @param args
+     */
+    public static boolean write(OutputStream os, AbsUserCache<?> lur) {
+        PrintStream ps;
+        if (os instanceof PrintStream) {
+            ps = (PrintStream)os;
+        } else {
+            ps = new PrintStream(os);
+        }
+        try {
+            ps.println("<?xml version='1.0' encoding='utf-8'?>");
+            ps.println("<!--");
+            ps.print(  "     Code Generated Tomcat Users and Roles from AT&T LUR on ");
+            ps.println(new Date());
+            ps.println(  "-->");
+            ps.println("<tomcat-users>");
+
+            // We loop through Users, but want to write Groups first... therefore, save off print
+            StringBuilder sb = new StringBuilder();
+
+            // Obtain all unique role names
+            HashSet<String> groups = new HashSet<>();
+            for (AbsUserCache<?>.DumpInfo di : lur.dumpInfo()) {
+                sb.append("\n  <user username=\"");
+                sb.append(di.user);
+                sb.append("\" roles=\"");
+                boolean first = true;
+                for (String role : di.perms) {
+                    groups.add(role);
+                    if (first)first = false;
+                    else sb.append(',');
+                    sb.append(role);
+                }
+                sb.append("\"/>");
+
+            }
+
+            // Print roles
+            for (String group : groups) {
+                ps.print("  <role rolename=\"");
+                ps.print(group);
+                ps.println("\"/>");
+            }
+
+            ps.println(sb);
+
+            ps.println("</tomcat-users>");
+            ps.flush();
+        } catch (Exception t) {
+            t.printStackTrace(ps);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     *
+     * Note: This method returns a String if there's an error, or null if ok.
+     * This unusual style is necessitated by the fact that any Exceptions thrown are likely to
+     * be unlogged and hidden from view, making debugging almost impossible.
+     *
+     * @param writeto
+     * @param up
+     * @return
+     */
+    public static String updateUsers(String writeto, LocalLur up) {
+        // Dump a Tomcat-user.xml lookalike (anywhere)
+        if (writeto!=null) {
+            // First read content
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            if (UsersDump.write(baos, up)) {
+                byte[] postulate = baos.toByteArray();
+                // now get contents of file
+                File file = new File(writeto);
+                boolean writeIt;
+                if (file.exists()) {
+                    try {
+                        FileInputStream fis = new FileInputStream(file);
+                        byte[] orig = new byte[(int)file.length()];
+                        int read;
+                        try {
+                            read = fis.read(orig);
+                        } finally {
+                            fis.close();
+                        }
+                        if (read<=0) {
+                            writeIt = false;
+                        } else {
+                            // Starting at third "<" (<tomcat-users> line)
+                            int startA=0, startB=0;
+                            for (int i=0;startA<orig.length && i<3;++startA) if (orig[startA]=='<')++i;
+                            for (int i=0;startB<orig.length && i<3;++startB) if (postulate[startB]=='<')++i;
+
+                            writeIt=orig.length-startA!=postulate.length-startB; // first, check if remaining length is the same
+                            while (!writeIt && startA<orig.length && startB<postulate.length) {
+                                if (orig[startA++]!=postulate[startB++])writeIt = true;
+                            }
+                        }
+                    } catch (Exception e) {
+                        writeIt = true;
+                    }
+                } else {
+                    writeIt = true;
+                }
+
+                if (writeIt) {
+                    try {
+                        FileOutputStream fos = new FileOutputStream(file);
+                        try {
+                            fos.write(postulate);
+                        } finally {
+                            fos.close();
+                        }
+                    } catch (IOException e) {
+                        return e.getMessage();
+                    }
+                }
+            }
+        }
+        return null; // no message means ok.
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/AUTHZ.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/AUTHZ.java
new file mode 100644 (file)
index 0000000..3c18f8d
--- /dev/null
@@ -0,0 +1,36 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.filter;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import jakarta.servlet.Servlet;
+
+@Target({TYPE})
+@Retention(RUNTIME)
+public @interface AUTHZ {
+    Class<? extends Servlet> value();
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/AUTHZServlet.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/AUTHZServlet.java
new file mode 100644 (file)
index 0000000..952c11e
--- /dev/null
@@ -0,0 +1,98 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.filter;
+
+import java.io.IOException;
+
+import jakarta.servlet.Servlet;
+import jakarta.servlet.ServletConfig;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+/**
+ *
+ * @author Jonathan
+ *
+ */
+public class AUTHZServlet<S extends Servlet> implements Servlet {
+    private String[] roles;
+    private Servlet delegate;
+
+    protected AUTHZServlet(Class<S> cls) {
+        try {
+            delegate = cls.newInstance();
+        } catch (Exception e) {
+            delegate = null;
+        }
+        RolesAllowed rolesAllowed = cls.getAnnotation(RolesAllowed.class);
+        if (rolesAllowed == null) {
+            roles = null;
+        } else {
+            roles = rolesAllowed.value();
+        }
+    }
+
+    public void init(ServletConfig sc) throws ServletException {
+        if (delegate == null) {
+            throw new ServletException("Invalid Servlet Delegate");
+        }
+        delegate.init(sc);
+    }
+
+    public ServletConfig getServletConfig() {
+        return delegate.getServletConfig();
+    }
+
+    public String getServletInfo() {
+        return delegate.getServletInfo();
+    }
+
+    public void service(ServletRequest req, ServletResponse resp) throws ServletException, IOException {
+        if (roles == null) {
+            delegate.service(req, resp);
+            return;
+        }
+
+        // Validate
+        try {
+            HttpServletRequest hreq = (HttpServletRequest)req;
+            for (String role : roles) {
+                if (hreq.isUserInRole(role)) {
+                    delegate.service(req, resp);
+                    return;
+                }
+            }
+
+            ((HttpServletResponse)resp).sendError(403); // forbidden
+        } catch (ClassCastException e) {
+            throw new ServletException("JASPIServlet only supports HTTPServletRequest/HttpServletResponse");
+        }
+    }
+
+    public void destroy() {
+        delegate.destroy();
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/AccessGetter.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/AccessGetter.java
new file mode 100644 (file)
index 0000000..36370c4
--- /dev/null
@@ -0,0 +1,35 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+package org.onap.ccsdk.apps.cadi.filter;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.config.Get;
+
+public class AccessGetter implements Get {
+    private final Access access;
+    public AccessGetter(Access access) {
+        this.access = access;
+    }
+    public String get(String name, String def, boolean print) {
+        return access.getProperty(name, def);
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/CadiApiEnforcementFilter.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/CadiApiEnforcementFilter.java
new file mode 100644 (file)
index 0000000..2e37641
--- /dev/null
@@ -0,0 +1,136 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ */
+package org.onap.ccsdk.apps.cadi.filter;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.Access.Level;
+import org.onap.ccsdk.apps.cadi.ServletContextAccess;
+import org.onap.ccsdk.apps.cadi.config.Config;
+import org.onap.ccsdk.apps.cadi.util.Split;
+
+/**
+ * This filter allows one to protect the APIs from data stored in AAF
+ *
+ * @author Instrumental(Jonathan)
+ */
+public class CadiApiEnforcementFilter implements Filter {
+    private String type;
+    private Map<String,List<String>> publicPaths;
+    private Access access;
+
+
+    public CadiApiEnforcementFilter(Access access, String enforce) throws ServletException {
+        this.access = access;
+        init(enforce);
+    }
+
+
+    @Override
+    public void init(FilterConfig fc) throws ServletException {
+        init(fc.getInitParameter(Config.CADI_API_ENFORCEMENT));
+        // need the Context for Logging, instantiating ClassLoader, etc
+        ServletContextAccess sca=new ServletContextAccess(fc);
+        if (access==null) {
+            access = sca;
+        }
+    }
+
+    private void init(final String ptypes) throws ServletException {
+        if(ptypes==null) {
+            throw new ServletException("CadiApiEnforcement requires " + Config.CADI_API_ENFORCEMENT + " property");
+        }
+        String[] full = Split.splitTrim(';', ptypes);
+        if(full.length==0) {
+            throw new ServletException(Config.CADI_API_ENFORCEMENT + " property is empty");
+        }
+        if(full.length>0) {
+            type=full[0];
+        }
+        publicPaths = new TreeMap<String,List<String>>();
+        if(full.length>1) {
+            for(int i=1;i<full.length;++i) {
+                String pubArray[] = Split.split(':', full[i]);
+                if(pubArray.length==2) {
+                    List<String> ls = publicPaths.get(pubArray[0]);
+                    if(ls==null) {
+                        ls = new ArrayList<String>();
+                        publicPaths.put(pubArray[0], ls);
+                    }
+                    ls.add(pubArray[1]);
+                }
+            }
+        }
+    }
+
+
+    @Override
+    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain fc) throws IOException, ServletException {
+        HttpServletRequest hreq = (HttpServletRequest)req;
+        final String meth = hreq.getMethod();
+        String path = hreq.getContextPath()+hreq.getPathInfo();
+
+        if(path == null || path.isEmpty() || "null".equals(path))
+            path = hreq.getRequestURI().substring(hreq.getContextPath().length());
+
+        List<String> list = publicPaths.get(meth);
+        if(list!=null) {
+            for( String p : publicPaths.get(meth)) {
+                if(path.startsWith(p)) {
+                    access.printf(Level.INFO, "%s accessed public API %s %s\n",
+                            hreq.getUserPrincipal().getName(),
+                            meth,
+                            path);
+                        fc.doFilter(req, resp);
+                        return;
+                }
+            }
+        }
+        if(hreq.isUserInRole(type + '|'+path+'|'+meth)) {
+            access.printf(Level.INFO, "%s is allowed access to %s %s\n",
+                hreq.getUserPrincipal().getName(),
+                meth,
+                path);
+            fc.doFilter(req, resp);
+        } else {
+            access.printf(Level.AUDIT, "%s is denied access to %s %s\n",
+                    hreq.getUserPrincipal().getName(),
+                    meth,
+                    path);
+        }
+    }
+
+    @Override
+    public void destroy() {
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/CadiFilter.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/CadiFilter.java
new file mode 100644 (file)
index 0000000..35d59d5
--- /dev/null
@@ -0,0 +1,358 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.filter;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.List;
+
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.Access.Level;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.CadiWrap;
+import org.onap.ccsdk.apps.cadi.LocatorException;
+import org.onap.ccsdk.apps.cadi.Lur;
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.ServletContextAccess;
+import org.onap.ccsdk.apps.cadi.TrustChecker;
+import org.onap.ccsdk.apps.cadi.config.Config;
+import org.onap.ccsdk.apps.cadi.config.Get;
+import org.onap.ccsdk.apps.cadi.taf.TafResp;
+import org.onap.ccsdk.apps.cadi.taf.TafResp.RESP;
+import org.onap.ccsdk.apps.cadi.util.Timing;
+
+/**
+ * CadiFilter
+ *
+ * This class implements Servlet Filter, and ties together CADI implementations
+ *
+ * This class can be used in a standard J2EE Servlet manner.  Optimal usage is for POJO operations, where
+ * one can enforce this Filter being first and primary.  Depending on the Container, it
+ * may be more effective, in some cases, to utilize features that allow earlier determination of
+ * AUTHN (Authorization).  An example would be "Tomcat Valve".  These implementations, however, should
+ * be modeled after the "init" and "doFilter" functions, and be kept up to date as this class changes.
+ *
+ *
+ * @author Jonathan
+ *
+ */
+public class CadiFilter implements Filter {
+    private static CadiHTTPManip httpChecker;
+    private static String[] pathExceptions;
+    private static List<Pair> mapPairs;
+    private Access access;
+    private Object[] additionalTafLurs;
+    private SideChain sideChain;
+    private static int count=0;
+
+    public Lur getLur() {
+        return httpChecker.getLur();
+    }
+
+    /**
+     * Construct a viable Filter
+     *
+     * Due to the vagaries of many containers, there is a tendency to create Objects and call "Init" on
+     * them at a later time.  Therefore, this object creates with an object that denies all access
+     * until appropriate Init happens, just in case the container lets something slip by in the meantime.
+     *
+     */
+    public CadiFilter() {
+        additionalTafLurs = CadiHTTPManip.noAdditional;
+    }
+
+    /**
+     * This constructor to be used when directly constructing and placing in HTTP Engine
+     *
+     * @param access
+     * @param moreTafLurs
+     * @throws ServletException
+     */
+    public CadiFilter(Access access, Object ... moreTafLurs) throws ServletException {
+        additionalTafLurs = moreTafLurs;
+        init(new AccessGetter(this.access = access));
+    }
+
+
+    /**
+     * Use this to pass in a PreContructed CADI Filter, but with initializing... let Servlet do it
+     * @param init
+     * @param access
+     * @param moreTafLurs
+     * @throws ServletException
+     */
+    public CadiFilter(boolean init, PropAccess access, Object ... moreTafLurs) throws ServletException {
+        this.access = access;
+        additionalTafLurs = moreTafLurs;
+        if (init) {
+            init(new AccessGetter(access));
+        }
+    }
+
+    /**
+     * Init
+     *
+     * Standard Filter "init" call with FilterConfig to obtain properties.  POJOs can construct a
+     * FilterConfig with the mechanism of their choice, and standard J2EE Servlet engines utilize this
+     * mechanism already.
+     */
+    //TODO Always validate changes against Tomcat AbsCadiValve and Jaspi CadiSAM Init functions
+    public void init(FilterConfig filterConfig) throws ServletException {
+        // need the Context for Logging, instantiating ClassLoader, etc
+        ServletContextAccess sca=new ServletContextAccess(filterConfig);
+        if (access==null) {
+            access = sca;
+        }
+
+        // Set Protected getter with base Access, for internal class instantiations
+        init(new FCGet(access, sca.context(), filterConfig));
+    }
+
+
+    @SuppressWarnings("unchecked")
+    protected void init(Get getter) throws ServletException {
+       sideChain = new SideChain();
+        // Start with the assumption of "Don't trust anyone".
+       TrustChecker tc = TrustChecker.NOTRUST; // default position
+       try {
+           Class<TrustChecker> ctc = (Class<TrustChecker>) Class.forName("org.onap.ccsdk.apps.cadi.aaf.v2_0.AAFTrustChecker");
+           if (ctc!=null) {
+               Constructor<TrustChecker> contc = ctc.getConstructor(Access.class);
+               if (contc!=null) {
+                   tc = contc.newInstance(access);
+               }
+           }
+       } catch (Exception e) {
+           access.log(Level.INIT, "AAFTrustChecker cannot be loaded",e.getMessage());
+       }
+
+       try {
+           Class<Filter> cf=null;
+           try {
+               cf= (Class<Filter>) Class.forName("org.onap.ccsdk.apps.cadi.oauth.OAuthFilter");
+               sideChain.add(cf.newInstance());
+           } catch (ClassNotFoundException e) {
+               access.log(Level.DEBUG, "OAuthFilter not enabled");
+           }
+       } catch (Exception e) {
+           access.log(Level.INIT, "AAFTrustChecker cannot be loaded",e.getMessage());
+       }
+
+
+        // Synchronize, because some instantiations call init several times on the same object
+        // In this case, the epiTaf will be changed to a non-NullTaf, and thus not instantiate twice.
+        synchronized(CadiHTTPManip.noAdditional /*will always remain same Object*/) {
+            ++count;
+            if (httpChecker == null) {
+                if (access==null) {
+                    access = new PropAccess();
+                }
+                try {
+                    httpChecker = new CadiHTTPManip(access,null /*reuseable Con*/,tc, additionalTafLurs);
+                } catch (CadiException | LocatorException e1) {
+                    throw new ServletException(e1);
+                }
+            } else if (access==null) {
+                access= httpChecker.getAccess();
+            }
+
+            /*
+             * Setup Authn Path Exceptions
+             */
+            if (pathExceptions==null) {
+                String str = getter.get(Config.CADI_NOAUTHN, null, true);
+                if (str!=null) {
+                    pathExceptions = str.split("\\s*:\\s*");
+                }
+            }
+
+            /*
+             * SETUP Permission Converters... those that can take Strings from a Vendor Product, and convert to appropriate AAF Permissions
+             */
+            if (mapPairs==null) {
+                String str = getter.get(Config.AAF_PERM_MAP, null, true);
+                if (str!=null) {
+                    String mstr = getter.get(Config.AAF_PERM_MAP, null, true);
+                    if (mstr!=null) {
+                        String map[] = mstr.split("\\s*:\\s*");
+                        if (map.length>0) {
+                            MapPermConverter mpc=null;
+                            int idx;
+                            mapPairs = new ArrayList<>();
+                            for (String entry : map) {
+                                if ((idx=entry.indexOf('='))<0) { // it's a Path, so create a new converter
+                                    access.log(Level.INIT,"Loading Perm Conversions for:",entry);
+                                    mapPairs.add(new Pair(entry,mpc=new MapPermConverter()));
+                                } else {
+                                    if (mpc!=null) {
+                                        mpc.map().put(entry.substring(0,idx),entry.substring(idx+1));
+                                    } else {
+                                        access.log(Level.ERROR,"cadi_perm_map is malformed; ",entry, "is skipped");
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        // Add API Enforcement Point
+        String enforce = getter.get(Config.CADI_API_ENFORCEMENT, null, true);
+        if(enforce!=null && enforce.length()>0) {
+            sideChain.add(new CadiApiEnforcementFilter(access,enforce));
+        }
+        // Remove Getter
+        getter = Get.NULL;
+    }
+
+    /**
+     * Containers call "destroy" when time to cleanup
+     */
+    public void destroy() {
+        // Synchronize, in case multiCadiFilters are used.
+        synchronized(CadiHTTPManip.noAdditional) {
+            if (--count<=0 && httpChecker!=null) {
+                httpChecker.destroy();
+                httpChecker=null;
+                access=null;
+                pathExceptions=null;
+            }
+        }
+    }
+
+    /**
+     * doFilter
+     *
+     * This is the standard J2EE invocation.  Analyze the request, modify response as necessary, and
+     * only call the next item in the filterChain if request is suitably Authenticated.
+     */
+    //TODO Always validate changes against Tomcat AbsCadiValve and Jaspi CadiSAM functions
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+        final long startAll = System.nanoTime();
+        long startCode, startValidate;
+        float code=0f, validate=0f;
+        String user = "n/a";
+        String tag = "";
+        TafResp tresp = null;
+        try {
+            HttpServletRequest hreq = (HttpServletRequest)request;
+            if (noAuthn(hreq)) {
+                startCode=System.nanoTime();
+                chain.doFilter(request, response);
+                code = Timing.millis(startCode);
+            } else {
+                HttpServletResponse hresp = (HttpServletResponse)response;
+                startValidate=System.nanoTime();
+                tresp = httpChecker.validate(hreq, hresp, hreq);
+                validate = Timing.millis(startValidate);
+                if (tresp.isAuthenticated()==RESP.IS_AUTHENTICATED) {
+                    user = tresp.getPrincipal().personalName();
+                    tag = tresp.getPrincipal().tag();
+                    CadiWrap cw = new CadiWrap(hreq, tresp, httpChecker.getLur(),getConverter(hreq));
+                    if (httpChecker.notCadi(cw, hresp)) {
+                        startCode=System.nanoTime();
+                        sideChain.doFilter(cw,response,chain);
+                        code = Timing.millis(startCode);
+                    }
+                }
+            }
+        } catch (ClassCastException e) {
+            throw new ServletException("CadiFilter expects Servlet to be an HTTP Servlet",e);
+        } finally {
+            if (tresp != null) {
+                access.printf(Level.INFO, "Trans: user=%s[%s],ip=%s,ms=%f,validate=%f,code=%f,result=%s",
+                    user,tag,request.getRemoteAddr(),
+                    Timing.millis(startAll),validate,code,tresp.isAuthenticated().toString());
+            } else {
+                access.printf(Level.INFO, "Trans: user=%s[%s],ip=%s,ms=%f,validate=%f,code=%f,result=FAIL",
+                    user,tag,request.getRemoteAddr(),
+                    Timing.millis(startAll),validate,code);
+            }
+        }
+    }
+
+
+    /**
+     * If PathExceptions exist, report if these should not have Authn applied.
+     * @param hreq
+     * @return
+     */
+    private boolean noAuthn(HttpServletRequest hreq) {
+        if (pathExceptions!=null) {
+            String pi = hreq.getPathInfo();
+            if (pi==null) {
+                // Attempt to get from URI only  (Daniel Rose)
+                pi = hreq.getRequestURI().substring(hreq.getContextPath().length());
+                if(pi==null) {
+                    // Nothing works.
+                    return false; // JBoss sometimes leaves null
+                }
+            }
+            for (String pe : pathExceptions) {
+                if (pi.startsWith(pe))return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Get Converter by Path
+     */
+    private PermConverter getConverter(HttpServletRequest hreq) {
+        if (mapPairs!=null) {
+            String pi = hreq.getPathInfo();
+            if (pi !=null) {
+                for (Pair p: mapPairs) {
+                    if (pi.startsWith(p.name))return p.pc;
+                }
+            }
+        }
+        return NullPermConverter.singleton();
+    }
+
+    /**
+     * store PermConverters by Path prefix
+     * @author Jonathan
+     *
+     */
+    private class Pair {
+        public Pair(String key, PermConverter pc) {
+            name = key;
+            this.pc = pc;
+        }
+        public String name;
+        public PermConverter pc;
+    }
+
+}
+
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/CadiHTTPManip.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/CadiHTTPManip.java
new file mode 100644 (file)
index 0000000..91760af
--- /dev/null
@@ -0,0 +1,221 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.filter;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.Access.Level;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.CadiWrap;
+import org.onap.ccsdk.apps.cadi.Connector;
+import org.onap.ccsdk.apps.cadi.CredVal;
+import org.onap.ccsdk.apps.cadi.LocatorException;
+import org.onap.ccsdk.apps.cadi.Lur;
+import org.onap.ccsdk.apps.cadi.Taf;
+import org.onap.ccsdk.apps.cadi.TrustChecker;
+import org.onap.ccsdk.apps.cadi.config.Config;
+import org.onap.ccsdk.apps.cadi.config.SecurityInfoC;
+import org.onap.ccsdk.apps.cadi.lur.EpiLur;
+import org.onap.ccsdk.apps.cadi.taf.HttpTaf;
+import org.onap.ccsdk.apps.cadi.taf.TafResp;
+import org.onap.ccsdk.apps.cadi.util.UserChainManip;
+
+/**
+ * Encapsulate common HTTP Manipulation Behavior.  It will appropriately set
+ * HTTPServletResponse for Redirect or Forbidden, as needed.
+ *
+ * Further, this is useful, because it avoids multiple creates of Connections, where some Filters
+ * are created and destroyed regularly.
+ *
+ * @author Jonathan
+ *
+ */
+public class CadiHTTPManip {
+    private static final String ACCESS_DENIED = "Access Denied";
+    private static final String NO_TAF_WILL_AUTHORIZE = "No TAF will authorize";
+    private static final String AUTHENTICATION_FAILURE = "Authentication Failure";
+    private static final String AUTHENTICATING_VIA_REDIRECTION = "Authenticating via redirection";
+    private static final String MSG_FMT = "user=%s,ip=%s:%d,msg=\"%s: %s\"";
+    private static final String AUTHENTICATED = "Authenticated";
+    private static final String ACCESS_CADI_CONTROL = ".access|cadi|control";
+    private static final String METH = "OPTIONS";
+    private static final String CADI = "/cadi/";
+    private static final String CADI_CACHE_PRINT = "/cadi/cache/print";
+    private static final String CADI_CACHE_CLEAR = "/cadi/cache/clear";
+    private static final String CADI_LOG_SET = "/cadi/log/set/";
+    private static final Object LOCK = new Object();
+    private Access access;
+    private HttpTaf taf;
+    private CredVal up;
+    private Lur lur;
+    private String thisPerm,companyPerm,aaf_id;
+
+    public static final Object[] noAdditional = new Object[0]; // CadiFilter can be created each call in some systems
+
+
+    public CadiHTTPManip(Access access, Connector con, TrustChecker tc, Object ... additionalTafLurs) throws CadiException, LocatorException {
+        synchronized(LOCK) {
+            this.access = access;
+//            Get getter = new AccessGetter(access);
+            Config.setDefaultRealm(access);
+
+            aaf_id = access.getProperty(Config.CADI_ALIAS,access.getProperty(Config.AAF_APPID, null));
+            if (aaf_id==null) {
+                access.printf(Level.INIT, "%s is not set. %s can be used instead",Config.AAF_APPID,Config.CADI_ALIAS);
+            } else {
+                access.printf(Level.INIT, "%s is set to %s",Config.AAF_APPID,aaf_id);
+            }
+            String ns = aaf_id==null?null:UserChainManip.idToNS(aaf_id);
+            if (ns!=null) {
+                thisPerm = ns+ACCESS_CADI_CONTROL;
+                int dot = ns.indexOf('.');
+                if (dot>=0) {
+                    int dot2=ns.indexOf('.',dot+1);
+                    if (dot2<0) {
+                        dot2=dot;
+                    }
+                    companyPerm = ns.substring(0, dot2)+ACCESS_CADI_CONTROL;
+                } else {
+                    companyPerm = "com"+ACCESS_CADI_CONTROL;
+                }
+            } else {
+                thisPerm = companyPerm = "com"+ACCESS_CADI_CONTROL;
+            }
+            SecurityInfoC<HttpURLConnection> si;
+            si = SecurityInfoC.instance(access, HttpURLConnection.class);
+
+            lur = Config.configLur(si, con, additionalTafLurs);
+
+            tc.setLur(lur);
+            if (lur instanceof EpiLur) {
+                up = ((EpiLur)lur).getUserPassImpl();
+            } else if (lur instanceof CredVal) {
+                up = (CredVal)lur;
+            } else {
+                up = null;
+            }
+            taf = Config.configHttpTaf(con,si, tc, up, lur, additionalTafLurs);
+        }
+    }
+
+    public TafResp validate(HttpServletRequest hreq, HttpServletResponse hresp, Object state) throws IOException {
+        TafResp tresp = taf.validate(Taf.LifeForm.LFN, hreq, hresp);
+        switch(tresp.isAuthenticated()) {
+            case IS_AUTHENTICATED:
+                access.printf(Level.DEBUG,MSG_FMT,tresp.getTarget(),hreq.getRemoteAddr(),
+                    hreq.getRemotePort(),AUTHENTICATED,tresp.desc());
+                break;
+            case TRY_AUTHENTICATING:
+                switch (tresp.authenticate()) {
+                    case IS_AUTHENTICATED:
+                        access.printf(Level.DEBUG,MSG_FMT,tresp.getTarget(),hreq.getRemoteAddr(),
+                            hreq.getRemotePort(),AUTHENTICATED,tresp.desc());
+                        break;
+                    case HTTP_REDIRECT_INVOKED:
+                        access.printf(Level.DEBUG,MSG_FMT,tresp.getTarget(),hreq.getRemoteAddr(),
+                            hreq.getRemotePort(),AUTHENTICATING_VIA_REDIRECTION,tresp.desc());
+                        break;
+                    case NO_FURTHER_PROCESSING:
+                        access.printf(Level.AUDIT,MSG_FMT,tresp.getTarget(),hreq.getRemoteAddr(),
+                            hreq.getRemotePort(),AUTHENTICATION_FAILURE,tresp.desc());
+                        hresp.sendError(403, tresp.desc()); // Forbidden
+                        break;
+
+                    default:
+                        access.printf(Level.AUDIT,MSG_FMT,tresp.getTarget(),hreq.getRemoteAddr(),
+                            hreq.getRemotePort(),NO_TAF_WILL_AUTHORIZE,tresp.desc());
+                        hresp.sendError(403, tresp.desc()); // Forbidden
+                }
+                break;
+            case NO_FURTHER_PROCESSING:
+                access.printf(Level.AUDIT,MSG_FMT, tresp.getTarget(),hreq.getRemoteAddr(),
+                        hreq.getRemotePort(),NO_TAF_WILL_AUTHORIZE,tresp.desc());
+                hresp.sendError(403, ACCESS_DENIED); // FORBIDDEN
+                break;
+            default:
+                access.printf(Level.AUDIT,MSG_FMT, tresp.getTarget(),hreq.getRemoteAddr(),
+                        hreq.getRemotePort(),NO_TAF_WILL_AUTHORIZE,tresp.desc());
+                hresp.sendError(403, ACCESS_DENIED); // FORBIDDEN
+        }
+
+        return tresp;
+    }
+
+    public boolean notCadi(CadiWrap req, HttpServletResponse resp) {
+
+        String pathInfo = req.getPathInfo();
+        if (METH.equalsIgnoreCase(req.getMethod()) && pathInfo!=null && pathInfo.contains(CADI)) {
+            if (req.getUser().equals(aaf_id) || req.isUserInRole(thisPerm) || req.isUserInRole(companyPerm)) {
+                try {
+                    if (pathInfo.contains(CADI_CACHE_PRINT)) {
+                        resp.getOutputStream().println(lur.toString());
+                        resp.setStatus(200);
+                        return false;
+                    } else if (pathInfo.contains(CADI_CACHE_CLEAR)) {
+                        StringBuilder report = new StringBuilder();
+                        lur.clear(req.getUserPrincipal(), report);
+                        resp.getOutputStream().println(report.toString());
+                        resp.setStatus(200);
+                        return false;
+                    } else if (pathInfo.contains(CADI_LOG_SET))  {
+                        Level l;
+                        int slash = pathInfo.lastIndexOf('/');
+                        String level = pathInfo.substring(slash+1);
+                        try {
+                            l = Level.valueOf(level);
+                            access.printf(Level.AUDIT, "%s has set CADI Log Level to '%s'",req.getUser(),l.name());
+                            access.setLogLevel(l);
+                        } catch (IllegalArgumentException e) {
+                            access.printf(Level.AUDIT, "'%s' is not a valid CADI Log Level",level);
+                        }
+                        return false;
+                    }
+                } catch (IOException e) {
+                    access.log(e);
+                }
+            }
+        }
+        return true;
+    }
+
+    public Lur getLur() {
+        return lur;
+    }
+
+    public void destroy() {
+        access.log(Level.INFO,"CadiHttpChecker destroyed.");
+        if (lur!=null) {
+            lur.destroy();
+            lur=null;
+        }
+    }
+
+    public Access getAccess() {
+        return access;
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/FCGet.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/FCGet.java
new file mode 100644 (file)
index 0000000..d51954f
--- /dev/null
@@ -0,0 +1,76 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.filter;
+
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletContext;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.Access.Level;
+import org.onap.ccsdk.apps.cadi.config.Get;
+
+/*
+ * A private method to query the Filter config and if not exists, return the default.  This
+ * cleans up the initialization code.
+ */
+public class FCGet implements Get {
+    /**
+     *
+     */
+    private final Access access;
+    private FilterConfig filterConfig;
+    private ServletContext context;
+
+    public FCGet(Access access, ServletContext context, FilterConfig filterConfig) {
+        this.access = access;
+        this.context = context;
+        this.filterConfig = filterConfig;
+    }
+
+    public String get(String name, String def, boolean print) {
+        String str = null;
+        // Try Server Context First
+        if (context!=null) {
+            str = context.getInitParameter(name);
+        }
+
+        // Try Filter Context next
+        if (str==null && filterConfig != null) {
+            str = filterConfig.getInitParameter(name);
+        }
+
+        if (str==null) {
+            str = access.getProperty(name, def);
+        }
+        // Take def if nothing else
+        if (str==null) {
+            str = def;
+            // don't log defaults
+        } else {
+            str = str.trim(); // this is vital in Property File based values, as spaces can hide easily
+            if (print) {
+                access.log(Level.INFO,"Setting", name, "to", str);
+            }
+        }
+        return str;
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/MapBathConverter.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/MapBathConverter.java
new file mode 100644 (file)
index 0000000..1b13188
--- /dev/null
@@ -0,0 +1,175 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.filter;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.Access.Level;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.Symm;
+import org.onap.ccsdk.apps.cadi.util.CSV;
+import org.onap.ccsdk.apps.cadi.util.CSV.Visitor;
+import org.onap.ccsdk.apps.cadi.util.Holder;
+
+/**
+ * This Filter is designed to help MIGRATE users from systems that don't match the FQI style.
+ *
+ * Style 1, where just the ID is translated, i.e. OLD => new@something.onap.org, that is acceptable
+ * longer term, because it does not store Creds locally.  The passwords are in appropriate systems, but
+ * it's still painful operationally, though it does ease migration.
+ *
+ * Style 3, however, which is Direct match of Authorization Header to replacement, is only there
+ * because some passwords are simply not acceptable for AAF, (too easy, for instance), and it is
+ * not feasible to break Organization Password rules for a Migration.  Therefore, this method
+ * should not considered something that is in any way a permanent
+ *
+
+ *
+ * It goes without saying that any file with the password conversion should be protected by "400", etc.
+ *
+ * @author Instrumental (Jonathan)
+ *
+ */
+public class MapBathConverter {
+    private static final String BASIC = "Basic ";
+    private final Map<String,String> map;
+
+    /**
+     * Create with colon separated name value pairs
+     *  Enter the entire "Basic dXNlcjpwYXNz" "Authorization" header, where "dXNlcjpwYXNz" is
+     *  base64 encoded, which can be created with "cadi" tool (in jar)
+     *
+     *  The replacement should also be an exact replacement of what you want.  Recognize that
+     *  this should be TEMPORARY as you are storing credentials outside the users control.
+     *
+     * @param value
+     * @throws IOException
+     * @throws CadiException
+     */
+    public MapBathConverter(final Access access, final CSV csv) throws IOException, CadiException {
+        map = new TreeMap<>();
+        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+        final Date now = new Date();
+        csv.visit(new Visitor() {
+            @Override
+            public void visit(List<String> row) throws CadiException {
+                if(row.size()<3) {
+                    throw new CadiException("CSV file " + csv + " must have at least 2 Basic Auth columns and an Expiration Date(YYYY-MM-DD) in each row");
+                }
+                try {
+                    Date date = sdf.parse(row.get(2));
+                    String oldID = row.get(0);
+                    String newID = row.get(1);
+                    if(date.after(now)) {
+                        if(!oldID.startsWith(BASIC) && newID.startsWith(BASIC)) {
+                            throw new CadiException("CSV file " + csv + ": Uncredentialed ID " + idFromBasic(oldID,null) +
+                                                    " may not transfer to credentialed ID " + idFromBasic(newID,null));
+                        } else {
+                            map.put(oldID,newID);
+                            access.printf(Level.INIT, "ID Conversion from %s to %s enabled",
+                                    idFromBasic(oldID,null),
+                                    idFromBasic(newID,null));
+                        }
+                    } else {
+                        access.printf(Level.INIT, "ID Conversion from %s to %s has expired.",
+                                idFromBasic(oldID,null),
+                                idFromBasic(newID,null));
+                    }
+                } catch (ParseException e) {
+                    throw new CadiException("Cannot Parse Date: " + row.get(2));
+                } catch (IOException e) {
+                    throw new CadiException(e);
+                }
+            }
+        });
+    }
+
+    private static String idFromBasic(String bath, Holder<String> hpass) throws IOException, CadiException {
+        if(bath.startsWith(BASIC)) {
+            String cred = Symm.base64noSplit.decode(bath.substring(6));
+            int colon = cred.indexOf(':');
+            if(colon<0) {
+                throw new CadiException("Invalid Authentication Credential for " + cred);
+            }
+            if(hpass!=null) {
+                hpass.set(cred.substring(colon+1));
+            }
+            return cred.substring(0, colon);
+        } else {
+            return bath;
+        }
+    }
+
+    /**
+     * use to instantiate entries
+     *
+     * @return
+     */
+    public Map<String,String> map() {
+        return map;
+    }
+
+    public String convert(Access access, final String bath) {
+        String rv = map.get(bath);
+
+        String cred;
+        String tcred=null;
+        Holder<String> hpass=null;
+        try {
+            if(bath.startsWith(BASIC)) {
+                cred = idFromBasic(bath,(hpass=new Holder<String>(null)));
+                if(rv==null) {
+                    rv = map.get(cred);
+                }
+            } else {
+                cred = bath;
+            }
+
+            if(rv==null) {
+                // Nothing here, just return original
+                rv = bath;
+            } else {
+                if(rv.startsWith(BASIC)) {
+                    tcred = idFromBasic(rv,null);
+                } else {
+                    if(hpass!=null) {
+                        tcred = rv;
+                        rv = BASIC + Symm.base64noSplit.encode(rv+':'+hpass.get());
+                    }
+                }
+                if(tcred != null) {
+                    access.printf(Level.AUDIT, "ID %s converted to %s",cred,tcred);
+                }
+            }
+        } catch (IOException | CadiException e) {
+            access.log(e,"Invalid Authorization");
+        }
+        return rv==null?bath:rv;
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/MapPermConverter.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/MapPermConverter.java
new file mode 100644 (file)
index 0000000..72e20cb
--- /dev/null
@@ -0,0 +1,54 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.filter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class MapPermConverter implements PermConverter {
+    private HashMap<String,String> map;
+
+    /**
+     * Create with colon separated name value pairs
+     *  i.e. teAdmin=com.att.myNS.myPerm|*|*:teUser=...
+     *
+     * @param value
+     */
+    public MapPermConverter() {
+        map = new HashMap<>();
+    }
+
+    /**
+     * use to instantiate entries
+     *
+     * @return
+     */
+    public Map<String,String> map() {
+        return map;
+    }
+
+    public String convert(String minimal) {
+        String rv = map.get(minimal);
+        return (rv == null) ? minimal : rv;
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/NullPermConverter.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/NullPermConverter.java
new file mode 100644 (file)
index 0000000..6ddd1f3
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.filter;
+
+
+/**
+ * A NullPermConverter
+ *
+ * Obey the PermConverter Interface, but passed in "minimal" String is not converted.
+ *
+ * @author Jonathan
+ *
+ */
+public class NullPermConverter implements PermConverter {
+
+    private static final NullPermConverter singleton = new NullPermConverter();
+
+    private NullPermConverter() {}
+
+    public static NullPermConverter singleton() { return singleton; }
+
+    public String convert(String minimal) {
+        return minimal;
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/PathFilter.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/PathFilter.java
new file mode 100644 (file)
index 0000000..458a444
--- /dev/null
@@ -0,0 +1,180 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.filter;
+
+import java.io.IOException;
+
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.Access.Level;
+import org.onap.ccsdk.apps.cadi.config.Config;
+
+/**
+ * PathFilter
+ *
+ * This class implements Servlet Filter, and uses AAF to validate access to a Path.
+ *
+ * This class can be used in a standard J2EE Servlet manner.
+ *
+ * @author Jonathan, collaborating with Xue Gao
+ *
+ */
+public class PathFilter implements Filter {
+    private final Log log;
+
+    private ServletContext context;
+    private String aafType;
+    private String notAuthorizedMsg;
+
+    /**
+     * Construct a viable Filter for installing in Container WEB.XML, etc.
+     *
+     */
+    public PathFilter() {
+        log = new Log() {
+            public void info(String ... msg) {
+                context.log(build("INFO:", msg));
+            }
+            public void audit(String ... msg) {
+                context.log(build("AUDIT:", msg));
+            }
+            private String build(String type, String []msg) {
+                StringBuilder sb = new StringBuilder(type);
+                for (String s : msg) {
+                    sb.append(' ');
+                    sb.append(s);
+                }
+                return sb.toString();
+            }
+        };
+    }
+
+    /**
+     * Filter that can be constructed within Java
+     * @param access
+     */
+    public PathFilter(final Access access) {
+        log = new Log() {
+            public void info(String ... msg) {
+                access.log(Level.INFO, (Object[])msg);
+            }
+            public void audit(String ... msg) {
+                access.log(Level.AUDIT, (Object[])msg);
+            }
+        };
+    }
+
+    /**
+     * Init
+     *
+     * Standard Filter "init" call with FilterConfig to obtain properties.  POJOs can construct a
+     * FilterConfig with the mechanism of their choice, and standard J2EE Servlet engines utilize this
+     * mechanism already.
+     */
+    public void init(FilterConfig filterConfig) throws ServletException {
+        // need the Context for Logging, instantiating ClassLoader, etc
+        context = filterConfig.getServletContext();
+        StringBuilder sb = new StringBuilder();
+        StringBuilder err = new StringBuilder();
+        Object attr = context.getAttribute(Config.PATHFILTER_NS);
+        if (attr == null) {
+            err.append("PathFilter - pathfilter_ns is not set");
+        } else {
+            sb.append(attr.toString());
+        }
+
+        attr = context.getAttribute(Config.PATHFILTER_STACK);
+        if (attr == null) {
+            log.info("PathFilter - No pathfilter_stack set, ignoring");
+        } else {
+            sb.append('.');
+            sb.append(attr.toString());
+        }
+
+        attr = context.getAttribute(Config.PATHFILTER_URLPATTERN);
+        if (attr == null) {
+            log.info("PathFilter - No pathfilter_urlpattern set, defaulting to 'urlpattern'");
+            sb.append(".urlpattern");
+        } else {
+            sb.append('.');
+            sb.append(attr.toString());
+        }
+
+        log.info("PathFilter - AAF Permission Type is", sb.toString());
+
+        sb.append('|');
+
+        aafType = sb.toString();
+
+        attr = context.getAttribute(Config.PATHFILTER_NOT_AUTHORIZED_MSG);
+        if (attr == null) {
+            notAuthorizedMsg = "Forbidden - Not Authorized to access this Path";
+        } else {
+            notAuthorizedMsg = attr.toString();
+        }
+
+        if (err.length() > 0) {
+            throw new ServletException(err.toString());
+        }
+    }
+
+    private interface Log {
+        public void info(String ... msg);
+        public void audit(String ... msg);
+    }
+
+    /**
+     * doFilter
+     *
+     * This is the standard J2EE invocation.  Analyze the request, modify response as necessary, and
+     * only call the next item in the filterChain if request is suitably Authenticated.
+     */
+    //TODO Always validate changes against Tomcat AbsCadiValve and Jaspi CadiSAM functions
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+        HttpServletRequest hreq = (HttpServletRequest)request;
+        HttpServletResponse hresp = (HttpServletResponse)response;
+        String perm = aafType + hreq.getPathInfo() + '|' + hreq.getMethod();
+        if (hreq.isUserInRole(perm)) {
+            chain.doFilter(request, response);
+        } else {
+            log.audit("PathFilter has denied", hreq.getUserPrincipal().getName(), "access to", perm);
+            hresp.sendError(403, notAuthorizedMsg);
+        }
+    }
+
+    /**
+     * Containers call "destroy" when time to cleanup
+     */
+    public void destroy() {
+        log.info("PathFilter destroyed.");
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/PermConverter.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/PermConverter.java
new file mode 100644 (file)
index 0000000..825260c
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.filter;
+
+/**
+ * Convert a simplistic, single string Permission into an Enterprise Scoped Perm
+ *
+ * @author Jonathan
+ *
+ */
+public interface PermConverter {
+    public String convert(String minimal);
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/RolesAllowed.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/RolesAllowed.java
new file mode 100644 (file)
index 0000000..0ffd232
--- /dev/null
@@ -0,0 +1,56 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+/**
+ * RolesAllowed
+ *
+ * @author Jonathan
+ *
+ * Similar to Java EE's Spec from Annotations 1.1, 2.8
+ *
+ * That Spec, however, was geared towards being able to route calls to Methods on Objects, and thus needed a more refined
+ * sense of permissions hierarchy.  The same mechanism, however, can easily be achieved on single Servlet/Handlers in
+ * POJOs like Jetty by simply adding the Roles Allowed in a similar Annotation
+ *
+ */
+package org.onap.ccsdk.apps.cadi.filter;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * JASPI Style Annotation of RolesAllowed when the coding style is desired but actually including all
+ * JEE jars is not. If using actual JASPI, use official @interface classes, not this one...
+ *
+ * @author Jonathan
+ */
+@Target({TYPE})
+@Retention(RUNTIME)
+public @interface RolesAllowed {
+    /**
+     * Security role of the implementation, which doesn't have to be an EJB or CORBA like object.  Can be just a
+     * Handler
+     * @return
+     */
+    String[] value();
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/ServletImpl.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/ServletImpl.java
new file mode 100644 (file)
index 0000000..404f1e6
--- /dev/null
@@ -0,0 +1,56 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+/**
+ * RolesAllowed
+ *
+ * @author Jonathan
+ *
+ * Similar to Java EE's Spec from Annotations 1.1, 2.8
+ *
+ * That Spec, however, was geared towards being able to route calls to Methods on Objects, and thus needed a more refined
+ * sense of permissions hierarchy.  The same mechanism, however, can easily be achieved on single Servlet/Handlers in
+ * POJOs like Jetty by simply adding the Roles Allowed in a similar Annotation
+ *
+ */
+package org.onap.ccsdk.apps.cadi.filter;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import jakarta.servlet.Servlet;
+
+/**
+ *
+ * @author Jonathan
+ */
+@Target({TYPE})
+@Retention(RUNTIME)
+public @interface ServletImpl {
+    /**
+     * Security role of the implementation, which doesn't have to be an EJB or CORBA like object.  Can be just a
+     * Handler
+     * @return
+     */
+    Class<? extends Servlet> value();
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/SideChain.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/filter/SideChain.java
new file mode 100644 (file)
index 0000000..39b82cc
--- /dev/null
@@ -0,0 +1,74 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ */
+
+package org.onap.ccsdk.apps.cadi.filter;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+
+import org.onap.ccsdk.apps.cadi.util.Holder;
+
+/**
+ * Add various Filters by CADI Property not in the official Chain
+ *
+ * @author Instrumental(Jonathan)
+ *
+ */
+public class SideChain {
+    private List<Filter> sideChain;
+
+    public SideChain() {
+        sideChain = new ArrayList<Filter>();
+    }
+
+    public void add(Filter f) {
+        sideChain.add(f);
+    }
+
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {
+        final Holder<Boolean> hbool = new Holder<Boolean>(Boolean.TRUE);
+        FilterChain truth = new FilterChain() {
+            @Override
+            public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
+               hbool.set(Boolean.TRUE);
+            }
+            public String toString() {
+                return hbool.get().toString();
+            }
+        };
+        for(Filter f : sideChain) {
+            hbool.set(Boolean.FALSE);
+            f.doFilter(request, response, truth);
+            if(!hbool.get()) {
+                return;
+            }
+        }
+        if(hbool.get()) {
+            chain.doFilter(request, response);
+        }
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/lur/ConfigPrincipal.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/lur/ConfigPrincipal.java
new file mode 100644 (file)
index 0000000..3b8f04c
--- /dev/null
@@ -0,0 +1,69 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.lur;
+
+import java.io.IOException;
+import java.security.Principal;
+
+import org.onap.ccsdk.apps.cadi.GetCred;
+import org.onap.ccsdk.apps.cadi.Symm;
+
+public class ConfigPrincipal implements Principal, GetCred {
+    private String name;
+    private byte[] cred;
+    private String content;
+
+    public ConfigPrincipal(String name, String passwd) {
+        this.name = name;
+        this.cred = passwd.getBytes();
+        content = null;
+    }
+
+    public ConfigPrincipal(String name, byte[] cred) {
+        this.name = name;
+        this.cred = cred;
+        content = null;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public byte[] getCred() {
+        return cred;
+    }
+
+    public String toString() {
+        return name;
+    }
+
+    public String getAsBasicAuthHeader() throws IOException {
+        if (content ==null) {
+            String s = name + ':' + new String(cred);
+            content = "Basic " + Symm.base64.encode(s);
+        } else if (!content.startsWith("Basic ")) { // content is the saved password from construction
+            String s = name + ':' + content;
+            content = "Basic " + Symm.base64.encode(s);
+        }
+        return content;
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/lur/EpiLur.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/lur/EpiLur.java
new file mode 100644 (file)
index 0000000..3e26fd4
--- /dev/null
@@ -0,0 +1,169 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.lur;
+
+import java.security.Principal;
+import java.util.List;
+
+import org.onap.ccsdk.apps.cadi.CachingLur;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.CredVal;
+import org.onap.ccsdk.apps.cadi.Lur;
+import org.onap.ccsdk.apps.cadi.Permission;
+
+/**
+ * EpiLUR
+ *
+ * Short for "Epic LUR". Be able to run through a series of LURs to obtain the validation needed.
+ *
+ * The pun is better for the other pattern... "TAF" (aka EpiTaf), but it's still the larger picture of
+ * LURs that will be accomplished.
+ *
+ * FYI, the reason we separate LURs, rather than combine, is that Various User Repository Resources have
+ * different Caching requirements.  For instance, the Local User Repo (with stand alone names), never expire, but might be
+ * refreshed with a change in Configuration File, while the Remote Service based LURs will need to expire at prescribed intervals
+ *
+ * @author Jonathan
+ *
+ */
+public final class EpiLur implements Lur {
+    private final Lur[] lurs;
+
+    /**
+     * EpiLur constructor
+     *
+     * Construct the EpiLur from variable TAF parameters
+     * @param lurs
+     * @throws CadiException
+     */
+    public EpiLur(Lur ... lurs) throws CadiException{
+        this.lurs = lurs;
+        if (lurs.length==0) throw new CadiException("Need at least one Lur implementation in constructor");
+    }
+
+    public boolean fish(Principal bait, Permission ... pond) {
+        if (pond==null) {
+            return false;
+        }
+        boolean rv = false;
+        Lur lur;
+        for (int i=0;!rv && i<lurs.length;++i) {
+            rv = (lur = lurs[i]).fish(bait, pond);
+            if (!rv && lur.handlesExclusively(pond)) break;
+        }
+        return rv;
+    }
+
+    public void fishAll(Principal bait, List<Permission> permissions) {
+        for (Lur lur : lurs) {
+            lur.fishAll(bait, permissions);
+        }
+    }
+
+    public void destroy() {
+        for (Lur lur : lurs) {
+            lur.destroy();
+        }
+    }
+
+    /**
+     * Return the first Lur (if any) which also implements UserPass
+     * @return
+     */
+    public CredVal getUserPassImpl() {
+        for (Lur lur : lurs) {
+            if (lur instanceof CredVal) {
+                return (CredVal)lur;
+            }
+        }
+        return null;
+    }
+
+    // Never needed... Only EpiLur uses...
+    public boolean handlesExclusively(Permission ... pond) {
+        return false;
+    }
+
+    /**
+     * Get Lur for index.  Returns null if out of range
+     * @param idx
+     * @return
+     */
+    public Lur get(int idx) {
+        if (idx>=0 && idx<lurs.length) {
+            return lurs[idx];
+        }
+        return null;
+    }
+
+    public boolean handles(Principal p) {
+        for (Lur l : lurs) {
+            if (l.handles(p)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public void remove(String id) {
+        for (Lur l : lurs) {
+            if (l instanceof CachingLur) {
+                ((CachingLur<?>)l).remove(id);
+            }
+        }
+    }
+
+    public Lur subLur(Class<? extends Lur> cls ) {
+        for (Lur l : lurs) {
+            if (l.getClass().isAssignableFrom(cls)) {
+                return l;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Permission createPerm(String p) {
+        return new LocalPermission(p);
+    }
+
+    /* (non-Javadoc)
+     * @see org.onap.ccsdk.apps.cadi.Lur#clear(java.security.Principal, java.lang.StringBuilder)
+     */
+    @Override
+    public void clear(Principal p, StringBuilder report) {
+        for (Lur lur : lurs) {
+            lur.clear(p, report);
+        }
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        for (Lur lur : lurs) {
+            sb.append(lur.getClass().getSimpleName());
+            sb.append(": Report\n");
+            sb.append(lur.toString());
+            sb.append('\n');
+        }
+        return sb.toString();
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/lur/LocalLur.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/lur/LocalLur.java
new file mode 100644 (file)
index 0000000..1a1af6c
--- /dev/null
@@ -0,0 +1,221 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.lur;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.onap.ccsdk.apps.cadi.AbsUserCache;
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.CredVal;
+import org.onap.ccsdk.apps.cadi.Hash;
+import org.onap.ccsdk.apps.cadi.Lur;
+import org.onap.ccsdk.apps.cadi.Permission;
+import org.onap.ccsdk.apps.cadi.User;
+import org.onap.ccsdk.apps.cadi.Access.Level;
+import org.onap.ccsdk.apps.cadi.config.Config;
+
+/**
+ * An in-memory Lur that can be configured locally with User info via properties, similar to Tomcat-users.xml mechanisms.
+ *
+ * @author Jonathan
+ *
+ */
+public final class LocalLur extends AbsUserCache<LocalPermission> implements Lur, CredVal {
+    public static final String SEMI = "\\s*;\\s*";
+    public static final String COLON = "\\s*:\\s*";
+    public static final String COMMA = "\\s*,\\s*";
+    public static final String PERCENT = "\\s*%\\s*";
+
+    // Use to quickly determine whether any given group is supported by this LUR
+    private final Set<String> supportingGroups;
+    private String supportedRealm;
+
+    /**
+     * Construct by building structure, see "build"
+     *
+     * Reconstruct with "build"
+     *
+     * @param userProperties
+     * @param groupProperties
+     * @param decryptor
+     * @throws IOException
+     */
+    public LocalLur(Access access, String userProperties, String groupProperties) throws IOException {
+        super(access, 0, 0, Integer.MAX_VALUE);  // data doesn't expire
+        supportedRealm = access.getProperty(Config.BASIC_REALM, "localized");
+        supportingGroups = new TreeSet<>();
+
+        if (userProperties != null) {
+            parseUserProperties(userProperties);
+        }
+
+        if (groupProperties != null) {
+            parseGroupProperties(groupProperties);
+        }
+    }
+
+    public boolean validate(String user, CredVal.Type type, byte[] cred, Object state) {
+        if (cred == null) {
+            return false;
+        }
+        User<LocalPermission> usr = getUser(user, cred);
+        if (usr == null) {
+            return false;
+        }
+        // covers null as well as bad pass
+        if ((type == Type.PASSWORD) && (usr.principal instanceof ConfigPrincipal)) {;
+            return Hash.isEqual(cred, ((ConfigPrincipal)usr.principal).getCred());
+        }
+        return false;
+    }
+
+    //    @Override
+    public boolean fish(Principal bait, Permission ... pond) {
+        if (pond == null) {
+            return false;
+        }
+        for (Permission p : pond) {
+            if (handles(bait) && p instanceof LocalPermission) { // local Users only have LocalPermissions
+                User<LocalPermission> user = getUser(bait);
+                if (user != null) {
+                    return user.contains((LocalPermission)p);
+                }
+            }
+        }
+        return false;
+    }
+
+    // We do not want to expose the actual Group, so make a copy.
+    public void fishAll(Principal bait, List<Permission> perms) {
+        if (handles(bait)) {
+            User<LocalPermission> user = getUser(bait);
+            if (user != null) {
+                user.copyPermsTo(perms);
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.onap.ccsdk.apps.cadi.Lur#handles(java.security.Principal)
+     */
+    @Override
+    public boolean handles(Principal principal) {
+        if (principal == null) {
+            return false;
+        }
+        return principal.getName().endsWith(supportedRealm);
+    }
+
+    @Override
+    public boolean handlesExclusively(Permission ... pond) {
+        boolean rv = false;
+        for (Permission p : pond) {
+            if (rv=supportingGroups.contains(p.getKey())) {
+                break;
+            }
+        }
+        return rv;
+    }
+
+    /* (non-Javadoc)
+     * @see org.onap.ccsdk.apps.cadi.Lur#createPerm(java.lang.String)
+     */
+    @Override
+    public Permission createPerm(String p) {
+        return new LocalPermission(p);
+    }
+
+    private void parseUserProperties(String userProperties) throws IOException {
+        // For each User name...
+        for (String userProperty : userProperties.trim().split(SEMI)) {
+            String[] userInfo = userProperty.split(COLON, 2);
+            String[] userPass = userInfo[0].split(PERCENT, 2);
+            String userName = userPass[0];
+
+            byte[] password = null;
+            if (userPass.length > 1) {
+                password = access.decrypt(userPass[1], true).getBytes();
+                if (userName.indexOf('@') < 0) {
+                    userName += '@' + access.getProperty(Config.AAF_DEFAULT_REALM, Config.getDefaultRealm());
+                }
+            }
+            User<LocalPermission> usr;
+            usr = new User<>(new ConfigPrincipal(userName, password));
+            addUser(usr);
+            access.log(Level.INIT, "Local User:", usr.principal);
+
+            if (userInfo.length > 1) {
+                Map<String, Permission> newMap = usr.newMap();
+                for (String group : userInfo[1].split(COMMA)) {
+                    supportingGroups.add(group);
+                    usr.add(newMap, new LocalPermission(group));
+                }
+                usr.setMap(newMap);
+            }
+        }
+    }
+
+
+    private void parseGroupProperties(String groupProperties) throws IOException {
+        // For each Group name...
+        for (String group : groupProperties.trim().split(SEMI)) {
+            String[] groups = group.split(COLON, 2);
+            if (groups.length <= 1) {
+                continue;
+            }
+            supportingGroups.add(groups[0]);
+            LocalPermission p = new LocalPermission(groups[0]);
+
+            // Add all users (known by comma separators)
+            for (String groupMember : groups[1].split(COMMA)) {
+                // look for password, if so, put in passMap
+                String[] userPass = groupMember.split(PERCENT, 2);
+                String userName = userPass[0];
+                if (userName.indexOf('@') < 0) {
+                    userName += '@' + access.getProperty(Config.AAF_DEFAULT_REALM, Config.getDefaultRealm());
+                }
+
+                User<LocalPermission> usr = null;
+                byte[] password = null;
+                if (userPass.length > 1) {
+                    password = access.decrypt(userPass[1], true).getBytes();
+                }
+                usr = getUser(userName, password);
+                if (usr == null) {
+                    usr = new User<>(new ConfigPrincipal(userName, password));
+                    addUser(usr);
+                }
+                else {
+                    usr.principal = new ConfigPrincipal(userName, password);
+                }
+                usr.add(p);
+                access.log(Level.INIT, "Local User:", usr.principal);
+            }
+        }
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/lur/LocalPermission.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/lur/LocalPermission.java
new file mode 100644 (file)
index 0000000..a6aa112
--- /dev/null
@@ -0,0 +1,50 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.lur;
+
+import org.onap.ccsdk.apps.cadi.Permission;
+
+public class LocalPermission implements Permission {
+    private String key;
+
+    public LocalPermission(String role) {
+        this.key = role;
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public String toString() {
+        return key;
+    }
+
+    public boolean match(Permission p) {
+        return key.equals(p.getKey());
+    }
+
+    public String permType() {
+        return "LOCAL";
+    }
+
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/lur/NullLur.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/lur/NullLur.java
new file mode 100644 (file)
index 0000000..32c0920
--- /dev/null
@@ -0,0 +1,87 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.lur;
+
+import java.security.Principal;
+import java.util.List;
+
+import org.onap.ccsdk.apps.cadi.Lur;
+import org.onap.ccsdk.apps.cadi.Permission;
+
+public class NullLur implements Lur {
+    private static final Permission NULL = new Permission() {
+        @Override
+        public String permType() {
+            return "";
+        }
+
+        @Override
+        public String getKey() {
+            return "";
+        }
+
+        @Override
+        public boolean match(Permission p) {
+            return false;
+        }};
+
+    public boolean fish(Principal bait, Permission ... pond) {
+        // Well, for Jenkins, this is ok... It finds out it can't do J2EE Security, and then looks at it's own
+//        System.err.println("CADI's LUR has not been configured, but is still being called.  Access is being denied");
+        return false;
+    }
+
+    public void fishAll(Principal bait,    List<Permission> permissions) {
+    }
+
+    public void destroy() {
+    }
+
+    public boolean handlesExclusively(Permission ... pond) {
+        return false;
+    }
+
+    public boolean handles(Principal p) {
+        return false;
+    }
+
+    /* (non-Javadoc)
+     * @see org.onap.ccsdk.apps.cadi.Lur#createPerm(java.lang.String)
+     */
+    @Override
+    public Permission createPerm(String p) {
+        return NULL;
+    }
+
+    /* (non-Javadoc)
+     * @see org.onap.ccsdk.apps.cadi.Lur#clear(java.security.Principal, java.lang.StringBuilder)
+     */
+    @Override
+    public void clear(Principal p, StringBuilder report) {
+        report.append(NullLur.class.getSimpleName());
+        report.append('\n');
+    }
+
+    public String toString() {
+        return NullLur.class.getSimpleName() + '\n';
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/BasicPrincipal.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/BasicPrincipal.java
new file mode 100644 (file)
index 0000000..ef0499c
--- /dev/null
@@ -0,0 +1,133 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.principal;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Date;
+
+import org.onap.ccsdk.apps.cadi.BasicCred;
+import org.onap.ccsdk.apps.cadi.GetCred;
+import org.onap.ccsdk.apps.cadi.Symm;
+
+public class BasicPrincipal extends BearerPrincipal implements GetCred {
+    private static byte[] basic = "Basic ".getBytes();
+
+    private String name = null;
+    private String shortName = null;
+    private String domain;
+    private byte[] cred = null;
+    private long created;
+
+
+    public BasicPrincipal(String content,String defaultDomain) throws IOException {
+        created = System.currentTimeMillis();
+        ByteArrayInputStream bis = new ByteArrayInputStream(content.getBytes());
+        // Read past "Basic ", ensuring it starts with it.
+        for (int i=0;i<basic.length;++i) {
+            if (bis.read()!=basic[i]) {
+                name=content;
+                cred = null;
+                return;
+            }
+        }
+        BasicOS bos = new BasicOS(content.length());
+        Symm.base64.decode(bis,bos); // note: writes directly to name until ':'
+        if (name==null) throw new IOException("Invalid Coding");
+        else cred = bos.toCred();
+        int at;
+        if ((at=name.indexOf('@'))>0) {
+            domain=name.substring(at+1);
+            shortName=name.substring(0, at);
+        } else {
+            shortName = name;
+            domain=defaultDomain;
+            name = name + '@' + defaultDomain;
+        }
+    }
+
+    public BasicPrincipal(BasicCred bc, String domain) {
+        name = bc.getUser();
+        cred = bc.getCred();
+        this.domain = domain;
+    }
+
+    private class BasicOS extends OutputStream {
+        private boolean first = true;
+        private ByteArrayOutputStream baos;
+
+        public BasicOS(int size) {
+            baos = new ByteArrayOutputStream(size);
+        }
+
+        @Override
+        public void write(int b) throws IOException {
+            if (b==':' && first) {
+                first = false;
+                name = new String(baos.toByteArray());
+                baos.reset(); //
+            } else {
+                baos.write(b);
+            }
+        }
+
+        private byte[] toCred() {
+            return baos.toByteArray();
+        }
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getShortName() {
+        return shortName;
+    }
+
+    public String getDomain() {
+        return domain;
+    }
+
+    public byte[] getCred() {
+        return cred;
+    }
+
+    public long created() {
+        return created;
+    }
+
+    public String toString() {
+        return "Basic Authorization for " + name + " evaluated on " + new Date(created).toString();
+    }
+
+    @Override
+    public String tag() {
+        return "BAth";
+    }
+
+    @Override
+    public String personalName() {
+        return name;  // personalName not available with Basic Auth
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/BearerPrincipal.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/BearerPrincipal.java
new file mode 100644 (file)
index 0000000..4d41659
--- /dev/null
@@ -0,0 +1,33 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.principal;
+
+public abstract class BearerPrincipal extends TaggedPrincipal {
+    private String bearer = null;
+    public BearerPrincipal setBearer(String bearer) {
+        this.bearer = bearer;
+        return this;
+    }
+    public String getBearer() {
+        return bearer;
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/CachedBasicPrincipal.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/CachedBasicPrincipal.java
new file mode 100644 (file)
index 0000000..7df4696
--- /dev/null
@@ -0,0 +1,65 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.principal;
+
+import java.io.IOException;
+
+import org.onap.ccsdk.apps.cadi.BasicCred;
+import org.onap.ccsdk.apps.cadi.CachedPrincipal;
+import org.onap.ccsdk.apps.cadi.taf.HttpTaf;
+
+/**
+ * Cached Principals need to be able to revalidate in the Background
+ *
+ * @author Jonathan
+ *
+ */
+public class CachedBasicPrincipal extends BasicPrincipal implements CachedPrincipal {
+    private final HttpTaf creator;
+    private long timeToLive;
+    private long expires;
+
+    public CachedBasicPrincipal(HttpTaf creator, BasicCred bc, String domain, long timeToLive) {
+        super(bc, domain);
+        this.creator = creator;
+        this.timeToLive = timeToLive;
+        expires = System.currentTimeMillis()+timeToLive;
+    }
+
+    public CachedBasicPrincipal(HttpTaf creator, String content, String domain, long timeToLive) throws IOException {
+        super(content, domain);
+        this.creator = creator;
+        this.timeToLive = timeToLive;
+        expires = System.currentTimeMillis()+timeToLive;
+    }
+
+    public CachedPrincipal.Resp revalidate(Object state) {
+        Resp resp = creator.revalidate(this, state);
+        if (resp.equals(Resp.REVALIDATED))expires = System.currentTimeMillis()+timeToLive;
+        return resp;
+    }
+
+    public long expires() {
+        return expires;
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/Kind.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/Kind.java
new file mode 100644 (file)
index 0000000..702ac5f
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.principal;
+
+import java.security.Principal;
+
+public class Kind {
+    public static final char X509 = 'X';
+    public static final char OAUTH = 'O';
+    public static final char AAF_OAUTH='A';
+    public static final char BASIC_AUTH = 'B';
+    public static final char UNKNOWN = 'U';
+
+
+    public static char getKind(final Principal principal) {
+        Principal check;
+        if (principal instanceof TrustPrincipal) {
+            check = ((TrustPrincipal)principal).original();
+        } else {
+            check = principal;
+        }
+        if (check instanceof X509Principal) {
+            return X509;
+        }
+        if (check instanceof OAuth2FormPrincipal) {
+            // Note: if AAF, will turn into 'A'
+            return OAUTH;
+        }
+        if (check instanceof BasicPrincipal) {
+            return BASIC_AUTH;
+        }
+        return UNKNOWN;
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/OAuth2FormPrincipal.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/OAuth2FormPrincipal.java
new file mode 100644 (file)
index 0000000..a183633
--- /dev/null
@@ -0,0 +1,61 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.principal;
+
+public class OAuth2FormPrincipal extends TaggedPrincipal {
+    private final String username;
+    private final String client_id;
+
+    /*
+     * Note: client_id and username might be the same, if only authenticating the Client_ID
+     */
+    public OAuth2FormPrincipal(final String client_id, final String username) {
+        this.username = username;
+        this.client_id = client_id;
+    }
+
+    @Override
+    public String getName() {
+        return username;
+    }
+
+    public String client_id() {
+        return client_id;
+    }
+
+    @Override
+    public String tag() {
+        return "OAuth";
+    }
+
+    @Override
+    public String personalName() {
+        if (username!=null && username!=client_id) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(username);
+            sb.append('|');
+            sb.append(client_id);
+            return sb.toString();
+        }
+        return client_id;
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/StringTagLookup.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/StringTagLookup.java
new file mode 100644 (file)
index 0000000..d6bf861
--- /dev/null
@@ -0,0 +1,35 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+package org.onap.ccsdk.apps.cadi.principal;
+
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.principal.TaggedPrincipal.TagLookup;
+
+public class StringTagLookup implements TagLookup {
+    private String tag;
+    public StringTagLookup(final String tag) {
+        this.tag = tag;
+    }
+    @Override
+    public String lookup() throws CadiException {
+        return tag;
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/TaggedPrincipal.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/TaggedPrincipal.java
new file mode 100644 (file)
index 0000000..72b6e6d
--- /dev/null
@@ -0,0 +1,60 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+package org.onap.ccsdk.apps.cadi.principal;
+
+import java.security.Principal;
+
+import org.onap.ccsdk.apps.cadi.CadiException;
+
+public abstract class TaggedPrincipal implements Principal {
+
+    public TaggedPrincipal() {
+        tagLookup = null;
+    }
+
+    public TaggedPrincipal(final TagLookup tl) {
+        tagLookup = tl;
+    }
+
+    public abstract String tag();  // String representing what kind of Authentication occurred.
+
+    public interface TagLookup {
+        public String lookup() throws CadiException;
+    }
+
+    private TagLookup tagLookup;
+
+    public void setTagLookup(TagLookup tl) {
+        tagLookup = tl;
+    }
+
+    public String personalName() {
+        if (tagLookup == null) {
+            return getName();
+        }
+        try {
+            return tagLookup.lookup();
+        } catch (CadiException e) {
+            return getName();
+        }
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/TrustPrincipal.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/TrustPrincipal.java
new file mode 100644 (file)
index 0000000..1d875a2
--- /dev/null
@@ -0,0 +1,70 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.principal;
+
+import java.security.Principal;
+
+import org.onap.ccsdk.apps.cadi.UserChain;
+
+public class TrustPrincipal extends BearerPrincipal implements UserChain {
+    private final String name;
+    private final Principal original;
+    private String userChain;
+
+    public TrustPrincipal(final Principal actual, final String asName) {
+        this.original = actual;
+        name = asName.trim();
+        if (actual instanceof UserChain) {
+            UserChain uc = (UserChain)actual;
+            userChain = uc.userChain();
+        } else if (actual instanceof TaggedPrincipal) {
+            userChain=((TaggedPrincipal)actual).tag();
+        } else {
+            userChain = actual.getClass().getSimpleName();
+        }
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String userChain() {
+        return userChain;
+    }
+
+    public Principal original() {
+        return original;
+    }
+
+    @Override
+    public String tag() {
+        return userChain;
+    }
+
+    @Override
+    public String personalName() {
+        return original.getName() + '[' + userChain + ']';
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/UnAuthPrincipal.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/UnAuthPrincipal.java
new file mode 100644 (file)
index 0000000..5f6e0e4
--- /dev/null
@@ -0,0 +1,37 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.principal;
+
+import java.security.Principal;
+
+public class UnAuthPrincipal implements Principal {
+    private String name;
+
+    public UnAuthPrincipal(final String name) {
+        this.name = name;
+    }
+    @Override
+    public String getName() {
+        return name;
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/X509Principal.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/principal/X509Principal.java
new file mode 100644 (file)
index 0000000..245b27a
--- /dev/null
@@ -0,0 +1,112 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.principal;
+
+import java.io.IOException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.regex.Pattern;
+
+import org.onap.ccsdk.apps.cadi.GetCred;
+import org.onap.ccsdk.apps.cadi.taf.basic.BasicHttpTaf;
+
+public class X509Principal extends BearerPrincipal implements GetCred {
+    private static final Pattern pattern = Pattern.compile("[a-zA-Z0-9]*\\@[a-zA-Z0-9.]*");
+    private final X509Certificate cert;
+    private final String name;
+    private byte[] content;
+    private BasicHttpTaf bht;
+
+    public X509Principal(String identity, X509Certificate cert) {
+        name = identity;
+        content = null;
+        this.cert = cert;
+    }
+
+    public X509Principal(String identity, X509Certificate cert, byte[] content, BasicHttpTaf bht) {
+        name = identity;
+        this.content = content;
+        this.cert = cert;
+        this.bht = bht;
+    }
+
+    public X509Principal(X509Certificate cert, byte[] content, BasicHttpTaf bht) throws IOException {
+        this.content=content;
+        this.cert = cert;
+        String _name = null;
+        String subj = cert.getSubjectDN().getName();
+        int cn = subj.indexOf("OU=");
+        if (cn>=0) {
+            cn+=3;
+            int space = subj.indexOf(',',cn);
+            if (space>=0) {
+                String id = subj.substring(cn, space);
+                if (pattern.matcher(id).matches()) {
+                    _name = id;
+                }
+            }
+        }
+        if (_name==null) {
+            throw new IOException("X509 does not have Identity as CN");
+        }
+        name = _name;
+        this.bht = bht;
+    }
+
+    public String getAsHeader() throws IOException {
+        try {
+            if (content==null) {
+                content=cert.getEncoded();
+            }
+        } catch (CertificateEncodingException e) {
+            throw new IOException(e);
+        }
+        return "X509 " + content;
+    }
+
+    public String toString() {
+        return "X509 Authentication for " + name;
+    }
+
+
+    public byte[] getCred() {
+        try {
+            return content==null?(content=cert.getEncoded()):content;
+        } catch (CertificateEncodingException e) {
+            return null;
+        }
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String tag() {
+        return "x509";
+    }
+
+    public BasicHttpTaf getBasicHttpTaf() {
+        return bht;
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/AbsTafResp.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/AbsTafResp.java
new file mode 100644 (file)
index 0000000..478d7ea
--- /dev/null
@@ -0,0 +1,168 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.principal.TaggedPrincipal;
+import org.onap.ccsdk.apps.cadi.util.Timing;
+
+/**
+ * AbsTafResp
+ *
+ * Base class for TafResp (TAF Response Objects)
+ *
+ * @author Jonathan
+ *
+ */
+public abstract class AbsTafResp implements TafResp {
+
+    protected final Access access;
+    protected final String tafName;
+    // Note: Valid Resp is based on Principal being non-null
+    protected final TaggedPrincipal principal;
+    protected final String target;
+    protected final String desc;
+    private float timing;
+
+    /**
+     * AbsTafResp
+     *
+     * Set and hold
+     * Description (for logging)
+     * Principal (as created by derived class)
+     * Access (for access to underlying container, i.e. for Logging, auditing, ClassLoaders, etc)
+     *
+     * @param access
+     * @param tafname
+     * @param principal
+     * @param description
+     */
+    public AbsTafResp(Access access, String tafname, TaggedPrincipal principal, String description) {
+        this.access = access;
+        this.tafName = tafname;
+        this.principal = principal;
+        this.target = principal==null?"unknown":principal.getName();
+        this.desc = description;
+    }
+
+    /**
+     * AbsTafResp
+     *
+     * Set and hold
+     * Description (for logging)
+     * Principal (as created by derived class)
+     * Access (for access to underlying container, i.e. for Logging, auditing, ClassLoaders, etc)
+     *
+     * @param access
+     * @param tafname
+     * @param principal
+     * @param description
+     */
+    public AbsTafResp(Access access, String tafname, String target, String description) {
+        this.access = access;
+        this.tafName = tafname;
+        this.principal = null;
+        this.target = target;
+        this.desc = description;
+    }
+
+    /**
+     * isValid()
+     *
+     * Respond in the affirmative if the TAF was able to Authenticate
+     */
+    public boolean isValid() {
+        return principal != null;
+    }
+
+    /**
+     * desc()
+     *
+     * Respond with description of response as given by the TAF
+     */
+    public String desc() {
+        return desc;
+    }
+
+    /**
+     * isAuthenticated()
+     *
+     * Respond with the TAF's code of whether Authenticated, or suggested next steps
+     * default is either IS_AUTHENTICATED, or TRY_ANOTHER_TAF.  The TAF can overload
+     * and suggest others, such as "NO_FURTHER_PROCESSING", if it can detect that this
+     * is some sort of security breach (i.e. Denial of Service)
+     */
+    public RESP isAuthenticated() {
+        return principal==null?RESP.TRY_ANOTHER_TAF:RESP.IS_AUTHENTICATED;
+    }
+
+    /**
+     * getPrincipal()
+     *
+     * Return the principal created by the TAF based on Authentication.
+     *
+     * Returns "null" if Authentication failed (no principal)
+     */
+    public TaggedPrincipal getPrincipal() {
+        return principal;
+    }
+
+    /* (non-Javadoc)
+     * @see org.onap.ccsdk.apps.cadi.taf.TafResp#getTarget()
+     */
+    @Override
+    public String getTarget() {
+        return target;
+    }
+
+    /**
+     * getAccess()
+     *
+     * Get the Access object from the TAF, so that appropriate Logging, etc can be coordinated.
+     */
+    public Access getAccess() {
+        return access;
+    }
+
+    /* (non-Javadoc)
+     * @see org.onap.ccsdk.apps.cadi.taf.TafResp#isFailedAttempt()
+     */
+    public boolean isFailedAttempt() {
+        return false;
+    }
+
+    @Override
+    public float timing() {
+        return timing;
+    }
+
+    @Override
+    public void timing(final long start) {
+        timing = Timing.millis(start);
+    }
+
+    @Override
+    public String taf() {
+        return tafName;
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/EpiTaf.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/EpiTaf.java
new file mode 100644 (file)
index 0000000..b4102b0
--- /dev/null
@@ -0,0 +1,84 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf;
+
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.Taf;
+
+/**
+ * EpiTAF
+ *
+ * Short for "Epic TAF". Be able to run through a series of TAFs to obtain the validation needed.
+ *
+ * OK, the name could probably be better as "Tafs", like it was originally, but the pun was too
+ * irresistible for this author to pass up.
+ *
+ * @author Jonathan
+ *
+ */
+public class EpiTaf implements Taf {
+    private Taf[] tafs;
+
+    /**
+     * EpiTaf constructor
+     *
+     * Construct the EpiTaf from variable TAF parameters
+     * @param tafs
+     * @throws CadiException
+     */
+    public EpiTaf(Taf ... tafs) throws CadiException{
+        this.tafs = tafs;
+        if (tafs.length==0) throw new CadiException("Need at least one Taf implementation in constructor");
+    }
+
+    /**
+     * validate
+     *
+     * Respond with the first TAF to authenticate user based on variable info and "LifeForm" (is it
+     * a human behind an interface, or a server behind a protocol).
+     *
+     * If there is no TAF that can authenticate, respond with the first TAF that suggests it can
+     * establish an Authentication conversation (TRY_AUTHENTICATING).
+     *
+     * If no TAF declares either, respond with NullTafResp (which denies all questions)
+     */
+    public TafResp validate(LifeForm reading, String... info) {
+        TafResp tresp,firstTryAuth=null;
+        for (Taf taf : tafs) {
+            tresp = taf.validate(reading, info);
+            switch(tresp.isAuthenticated()) {
+                case TRY_ANOTHER_TAF:
+                    break;
+                case TRY_AUTHENTICATING:
+                    if (firstTryAuth==null)firstTryAuth=tresp;
+                    break;
+                default:
+                    return tresp;
+            }
+        }
+
+        // No TAFs configured, at this point.  It is safer at this point to be "not validated",
+        // rather than "let it go"
+        return firstTryAuth == null?NullTafResp.singleton():firstTryAuth;
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/HttpEpiTaf.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/HttpEpiTaf.java
new file mode 100644 (file)
index 0000000..ca792ab
--- /dev/null
@@ -0,0 +1,214 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf;
+
+import java.net.URI;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.Access.Level;
+import org.onap.ccsdk.apps.cadi.CachedPrincipal;
+import org.onap.ccsdk.apps.cadi.CachedPrincipal.Resp;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.Locator;
+import org.onap.ccsdk.apps.cadi.Taf.LifeForm;
+import org.onap.ccsdk.apps.cadi.TrustChecker;
+
+/**
+ * HttpEpiTaf
+ *
+ * An extension of the basic "EpiTAF" concept, check known HTTP Related TAFs for valid credentials
+ *
+ * @author Jonathan
+ *
+ */
+public class HttpEpiTaf implements HttpTaf {
+    private HttpTaf[] tafs;
+    private Access access;
+    private Locator<URI> locator;
+    private TrustChecker trustChecker;
+
+    /**
+     * HttpEpiTaf constructor
+     *
+     * Construct the HttpEpiTaf from variable Http specific TAF parameters
+
+     * @param tafs
+     * @throws CadiException
+     */
+    public HttpEpiTaf(Access access, Locator<URI> locator, TrustChecker tc, HttpTaf ... tafs) throws CadiException{
+        this.tafs = tafs;
+        this.access = access;
+        this.locator = locator;
+        this.trustChecker = tc;
+        // Establish what Header Property to look for UserChain/Trust Props
+
+        if (tafs.length == 0) {
+            throw new CadiException("Need at least one HttpTaf implementation in constructor");
+        }
+    }
+
+    /**
+     * validate
+     *
+     * Respond with the first Http specific TAF to authenticate user based on variable info
+     * and "LifeForm" (is it a human behind a browser, or a server utilizing HTTP Protocol).
+     *
+     * If there is no HttpTAF that can authenticate, respond with the first TAF that suggests it can
+     * establish an Authentication conversation (TRY_AUTHENTICATING) (Examples include a redirect to CSP
+     * Servers for CSP Cookie, or BasicAuth 401 response, suggesting User/Password for given Realm
+     * submission
+     *
+     * If no TAF declares either, respond with NullTafResp (which denies all questions)
+     */
+    public TafResp validate(LifeForm reading, HttpServletRequest req, HttpServletResponse resp) {
+        // Given a LifeForm Neutral, for HTTP, we need to discover true Life-Form Readings
+        if (reading == LifeForm.LFN) {
+            reading = tricorderScan(req);
+        }
+        TafResp tresp = null;
+        TafResp firstTry = null;
+        List<Redirectable> redirectables = null;
+        List<TafResp> log;
+        if (access.willLog(Level.DEBUG)) {
+            log = new ArrayList<>();
+        } else {
+            log = null;
+        }
+        try {
+            for (HttpTaf taf : tafs) {
+                final long start = System.nanoTime();
+                tresp = taf.validate(reading, req, resp);
+                addToLog(log, tresp, start);
+                switch(tresp.isAuthenticated()) {
+                    case TRY_ANOTHER_TAF:
+                        break; // and loop
+                    case TRY_AUTHENTICATING:
+                        if (tresp instanceof Redirectable) {
+                            if (redirectables == null) {
+                                redirectables = new ArrayList<>();
+                            }
+                            redirectables.add((Redirectable)tresp);
+                        } else if (firstTry == null) {
+                            firstTry = tresp;
+                        }
+                        break;
+                    case IS_AUTHENTICATED:
+                        tresp = trustChecker.mayTrust(tresp, req);
+                        return tresp;
+                    default:
+                        return tresp;
+                }
+            }
+        } finally {
+            printLog(log);
+        }
+
+        // If No TAFs configured, at this point.  It is safer at this point to be "not validated",
+        // rather than "let it go"
+        // Note: if exists, there will always be more than 0 entries, according to above code
+        if (redirectables == null) {
+            return (firstTry != null) ? firstTry : NullTafResp.singleton();
+        }
+
+        // If there is one Tryable entry then return it
+        if (redirectables.size() > 1) {
+            return LoginPageTafResp.create(access, locator, resp, redirectables);
+        } else {
+            return redirectables.get(0);
+        }
+    }
+
+    public boolean revalidate(Principal prin) throws Exception {
+        return false;
+    }
+
+    /*
+     * Since this is internal, we use a little Star Trek humor to indicate looking in the HTTP Request to see if we can determine what kind
+     * of "LifeForm" reading we can determine, i.e. is there a Human (CarbonBasedLifeForm) behind a browser, or is it mechanical
+     * id (SiliconBasedLifeForm)?  This makes a difference in some Authentication, i.e CSP, which doesn't work well for SBLFs
+     */
+    private LifeForm tricorderScan(HttpServletRequest req) {
+        // For simplicity's sake, we'll say Humans use FQDNs, not IPs.
+
+        // Current guess that only Browsers bother to set "Agent" codes that identify the kind of browser they are.
+        // If mechanical frameworks are found that populate this, then more advanced analysis may be required
+        // Jonathan 1/22/2013
+        String agent = req.getHeader("User-Agent");
+        if (agent != null && agent.startsWith("Mozilla")) { // covers I.E./Firefox/Safari/probably any other "advanced" Browser see http://en.wikipedia.org/wiki/User_agent
+            return LifeForm.CBLF;
+        }
+        return LifeForm.SBLF;                            // notably skips "curl","wget", (which is desired behavior.  We don't want to try CSP, etc on these)
+    }
+
+    public Resp revalidate(CachedPrincipal prin, Object state) {
+        Resp resp;
+        for (HttpTaf taf : tafs) {
+            resp = taf.revalidate(prin, state);
+            if (resp != Resp.NOT_MINE) {
+                return resp;
+            }
+//            switch(resp) {
+//                case NOT_MINE:
+//                    break;
+//                default:
+//                    return resp;
+//            }
+        }
+        return Resp.NOT_MINE;
+    }
+
+    private void addToLog(List<TafResp> log, final TafResp tresp, final long start) {
+        if (log == null) {
+            return;
+        }
+        tresp.timing(start);
+        log.add(tresp);
+    }
+
+    private void printLog(List<TafResp> log) {
+        if (log == null) {
+            return;
+        }
+        for (TafResp tresp : log) {
+            access.printf(Level.DEBUG, "%s: %s, ms=%f", tresp.taf(), tresp.desc(), tresp.timing());
+        }
+    }
+
+    /**
+     * List HttpTafs with their "toString" representations... primarily useful for Debugging in an IDE
+     * like Eclipse.
+     */
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        for (HttpTaf ht : tafs) {
+            sb.append(ht.toString());
+            sb.append(". ");
+        }
+        return sb.toString();
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/HttpTaf.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/HttpTaf.java
new file mode 100644 (file)
index 0000000..d2ac590
--- /dev/null
@@ -0,0 +1,60 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.onap.ccsdk.apps.cadi.CachedPrincipal;
+import org.onap.ccsdk.apps.cadi.Taf.LifeForm;
+
+/**
+ * A TAF which is in a specific HTTP environment in which the engine implements
+ * javax Servlet.
+ *
+ * Using the Http Request and Response interfaces takes the effort out of implementing in almost any kind of
+ * HTTP Container or Engine.
+ *
+ * @author Jonathan
+ *
+ */
+public interface HttpTaf {
+    /**
+     * validate
+     *
+     * Validate the Request, and respond with created TafResp object.
+     *
+     * @param reading
+     * @param req
+     * @param resp
+     * @return
+     */
+    public TafResp validate(LifeForm reading, HttpServletRequest req, HttpServletResponse resp);
+
+    /**
+     * Re-Validate Credential
+     *
+     * @param prin
+     * @return
+     */
+    public CachedPrincipal.Resp revalidate(CachedPrincipal prin,Object state);
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/LoginPageTafResp.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/LoginPageTafResp.java
new file mode 100644 (file)
index 0000000..43aac66
--- /dev/null
@@ -0,0 +1,100 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.List;
+
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.Locator;
+import org.onap.ccsdk.apps.cadi.Access.Level;
+import org.onap.ccsdk.apps.cadi.Locator.Item;
+
+public class LoginPageTafResp extends AbsTafResp {
+    private final HttpServletResponse httpResp;
+    private final String loginPageURL;
+
+    private LoginPageTafResp(Access access, final HttpServletResponse resp, String loginPageURL) {
+        super(access, "LoginPage","unknown", "Multiple Possible HTTP Logins available.  Redirecting to Login Choice Page");
+        httpResp = resp;
+        this.loginPageURL = loginPageURL;
+    }
+
+    @Override
+    public RESP authenticate() throws IOException {
+        httpResp.sendRedirect(loginPageURL);
+        return RESP.HTTP_REDIRECT_INVOKED;
+    }
+
+    @Override
+    public RESP isAuthenticated() {
+        return RESP.TRY_AUTHENTICATING;
+    }
+
+    public static TafResp create(Access access, Locator<URI> locator, final HttpServletResponse resp, List<Redirectable> redirectables) {
+        if (locator == null) {
+            if (!redirectables.isEmpty()) {
+                access.log(Level.DEBUG,"LoginPage Locator is not configured. Taking first Redirectable Taf");
+                return redirectables.get(0);
+            }
+            return NullTafResp.singleton();
+        }
+
+        try {
+            Item item = locator.best();
+            URI uri = locator.get(item);
+            if (uri == null) {
+                return NullTafResp.singleton();
+            }
+
+            StringBuilder sb = new StringBuilder(uri.toString());
+            String query = uri.getQuery();
+            boolean first = ((query == null) || (query.length() == 0));
+            for (Redirectable redir : redirectables) {
+                if (first) {
+                    sb.append('?');
+                    first = false;
+                }
+                else {
+                    sb.append('&');
+                }
+                sb.append(redir.get());
+            }
+            if (!redirectables.isEmpty()) {
+                return new LoginPageTafResp(access, resp, sb.toString());
+            }
+        } catch (Exception e) {
+            access.log(e, "Error deriving Login Page location");
+        }
+
+        return NullTafResp.singleton();
+    }
+
+    @Override
+    public String taf() {
+        return "LoginPage";
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/NullTaf.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/NullTaf.java
new file mode 100644 (file)
index 0000000..9fb1697
--- /dev/null
@@ -0,0 +1,64 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.onap.ccsdk.apps.cadi.CachedPrincipal;
+import org.onap.ccsdk.apps.cadi.Taf;
+import org.onap.ccsdk.apps.cadi.CachedPrincipal.Resp;
+
+
+/**
+ * This TAF is set at the very beginning of Filters and Valves so that if any configuration issues hit while
+ * starting, the default behavior is to shut down traffic rather than leaving an open hole
+ *
+ * @author Jonathan
+ *
+ */
+public class NullTaf implements Taf, HttpTaf {
+    // Singleton Pattern
+    public NullTaf() {}
+
+    /**
+     * validate
+     *
+     * Always Respond with a NullTafResp, which declares it is unauthenticated, and unauthorized
+     */
+    public TafResp validate(LifeForm reading, String... info) {
+        return NullTafResp.singleton();
+    }
+
+    /**
+     * validate
+     *
+     * Always Respond with a NullTafResp, which declares it is unauthenticated, and unauthorized
+     */
+    public TafResp validate(LifeForm reading, HttpServletRequest req, HttpServletResponse resp) {
+        return NullTafResp.singleton();
+    }
+
+    public Resp revalidate(CachedPrincipal prin, Object state) {
+        return Resp.NOT_MINE;
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/NullTafResp.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/NullTafResp.java
new file mode 100644 (file)
index 0000000..f93baee
--- /dev/null
@@ -0,0 +1,96 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf;
+
+import java.io.IOException;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.principal.TaggedPrincipal;
+
+/**
+ * A Null Pattern for setting responses to "Deny" before configuration is setup.
+ * @author Jonathan
+ *
+ */
+class NullTafResp implements TafResp {
+    private NullTafResp(){}
+
+    private static TafResp singleton = new NullTafResp();
+
+    public static TafResp singleton() {
+        return singleton;
+    }
+
+    public boolean isValid() {
+        return false;
+    }
+
+    public RESP isAuthenticated() {
+        return RESP.NO_FURTHER_PROCESSING;
+    }
+
+    public String desc() {
+        return "All Authentication denied";
+    }
+
+    public RESP authenticate() throws IOException {
+        return RESP.NO_FURTHER_PROCESSING;
+    }
+
+    public TaggedPrincipal getPrincipal() {
+        return null;
+    }
+
+    /* (non-Javadoc)
+     * @see org.onap.ccsdk.apps.cadi.taf.TafResp#getTarget()
+     */
+    @Override
+    public String getTarget() {
+        return "unknown";
+    }
+
+    public Access getAccess() {
+        return Access.NULL;
+    }
+
+    /* (non-Javadoc)
+     * @see org.onap.ccsdk.apps.cadi.taf.TafResp#isFailedAttempt()
+     */
+    public boolean isFailedAttempt() {
+        return true;
+    }
+
+    @Override
+    public float timing() {
+        return 0;
+    }
+
+    @Override
+    public void timing(long start) {
+    }
+
+    @Override
+    public String taf() {
+        return "NULL";
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/PuntTafResp.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/PuntTafResp.java
new file mode 100644 (file)
index 0000000..3b028c0
--- /dev/null
@@ -0,0 +1,97 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf;
+
+import java.io.IOException;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.principal.TaggedPrincipal;
+import org.onap.ccsdk.apps.cadi.util.Timing;
+
+/**
+ * A Punt Resp to make it fast and easy for a Taf to respond that it cannot handle a particular kind of
+ * request.  It is always the same object, so there is no cost for memory, etc.
+ * @author Jonathan
+ *
+ */
+public class PuntTafResp implements TafResp {
+    private final String name;
+    private final String desc;
+    private float timing;
+
+    public PuntTafResp(String name, String explanation) {
+        this.name = name;
+        desc = "Not processing this transaction: " + explanation;
+    }
+
+    public boolean isValid() {
+        return false;
+    }
+
+    public RESP isAuthenticated() {
+        return RESP.TRY_ANOTHER_TAF;
+    }
+
+    public String desc() {
+        return desc;
+    }
+
+    public RESP authenticate() throws IOException {
+        return RESP.TRY_ANOTHER_TAF;
+    }
+
+    public TaggedPrincipal getPrincipal() {
+        return null;
+    }
+
+    /* (non-Javadoc)
+     * @see org.onap.ccsdk.apps.cadi.taf.TafResp#getTarget()
+     */
+    @Override
+    public String getTarget() {
+        return "punt";
+    }
+
+    public Access getAccess() {
+        return NullTafResp.singleton().getAccess();
+    }
+
+    public boolean isFailedAttempt() {
+        return false;
+    }
+
+    @Override
+    public float timing() {
+        return timing;
+    }
+
+    @Override
+    public void timing(long start) {
+        timing = Timing.millis(start);
+    }
+
+    @Override
+    public String taf() {
+        return name;
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/Redirectable.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/Redirectable.java
new file mode 100644 (file)
index 0000000..358d6b2
--- /dev/null
@@ -0,0 +1,31 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf;
+
+public interface Redirectable extends TafResp {
+    /**
+     * Create a Redirectable URL entry prefaced by a URLEncoder.String for a Menu
+     * example:
+     * "Global Login=https://xxxx....."
+     */
+    public String get();
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/TafResp.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/TafResp.java
new file mode 100644 (file)
index 0000000..8092381
--- /dev/null
@@ -0,0 +1,116 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf;
+
+import java.io.IOException;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.principal.TaggedPrincipal;
+
+/**
+ * Response from Taf objects, which inform users what has happened and/or what should be done
+ *
+ * @author Jonathan
+ *
+ */
+public interface TafResp {
+    public static enum RESP {
+        IS_AUTHENTICATED,
+        NO_FURTHER_PROCESSING,
+        TRY_AUTHENTICATING,
+        TRY_ANOTHER_TAF,
+        FAIL,
+        // A note was made to avoid the response REDIRECT.  However, I have deemed that it is
+        // unavoidable when the underlying TAF did do a REDIRECT, because it requires a HTTP
+        // Service code to exit without modifying the Response any further.
+        // Therefore, I have changed this to indicate what HAS happened, with should accommodate
+        // both positions.  Jonathan 10/18/2012
+//        public static final int HTTP_REDIRECT_INVOKED = 11;
+        HTTP_REDIRECT_INVOKED,
+        HAS_PROCESSED};
+
+    /**
+     * Basic success check
+     * @return
+     */
+    public boolean isValid();
+
+    /**
+     *  String description of what has occurred (for logging/exceptions)
+     * @return
+     */
+    public String desc();
+
+    /**
+     * Check Response
+     * @return
+     */
+    public RESP isAuthenticated();
+
+    /**
+     * Authenticate, returning FAIL or Other Valid indication
+     *
+     * HTTP implementations should watch for "HTTP_REDIRECT_INVOKED", and end the HTTP call appropriately.
+     * @return
+     * @throws CadiException
+     */
+    public RESP authenticate() throws IOException;
+
+    /**
+     * Once authenticated, this object should hold a Principal created from the authorization
+     * @return
+     */
+    public TaggedPrincipal getPrincipal();
+
+    /** Target - when Authentication Fails, need to know what ID was being attempted
+     * @return
+     */
+    public String getTarget();
+
+    /**
+     * get the Access object which created this object, allowing the responder to appropriate Log, etc
+     */
+    public Access getAccess();
+
+    /**
+     * Be able to check if part of a Failed attempt
+     */
+    public boolean isFailedAttempt();
+
+    /**
+     * report how long this took
+     * @return
+     */
+    public float timing();
+
+    /**
+     * Set end of timing in Millis, given Nanos
+     * @param start
+     */
+    void timing(long start);
+
+    /**
+     * Support Taf Name
+     */
+    String taf();
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/TrustNotTafResp.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/TrustNotTafResp.java
new file mode 100644 (file)
index 0000000..4f2b040
--- /dev/null
@@ -0,0 +1,102 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf;
+
+import java.io.IOException;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.principal.TaggedPrincipal;
+import org.onap.ccsdk.apps.cadi.util.Timing;
+
+public class TrustNotTafResp implements TafResp {
+    private final TafResp delegate;
+    private final String desc;
+    private float timing;
+
+    public TrustNotTafResp(final TafResp delegate, final String desc) {
+        this.delegate = delegate;
+        this.desc = desc;
+    }
+
+    @Override
+    public boolean isValid() {
+        return false;
+    }
+
+    @Override
+    public String desc() {
+        return desc;
+    }
+
+    @Override
+    public RESP isAuthenticated() {
+        return RESP.NO_FURTHER_PROCESSING;
+    }
+
+    @Override
+    public RESP authenticate() throws IOException {
+        return RESP.NO_FURTHER_PROCESSING;
+    }
+
+    @Override
+    public TaggedPrincipal getPrincipal() {
+        return delegate.getPrincipal();
+    }
+
+    /* (non-Javadoc)
+     * @see org.onap.ccsdk.apps.cadi.taf.TafResp#getTarget()
+     */
+    @Override
+    public String getTarget() {
+        return delegate.getTarget();
+    }
+
+    @Override
+    public Access getAccess() {
+        return delegate.getAccess();
+    }
+
+    @Override
+    public boolean isFailedAttempt() {
+        return true;
+    }
+    @Override
+    public float timing() {
+        return timing;
+    }
+
+    @Override
+    public void timing(long start) {
+        timing = Timing.millis(start);
+    }
+
+    @Override
+    public String toString() {
+        return desc();
+    }
+
+    @Override
+    public String taf() {
+        return "TrustNot";
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/TrustTafResp.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/TrustTafResp.java
new file mode 100644 (file)
index 0000000..e3a4e80
--- /dev/null
@@ -0,0 +1,103 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf;
+
+import java.io.IOException;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.principal.TaggedPrincipal;
+import org.onap.ccsdk.apps.cadi.util.Timing;
+
+public class TrustTafResp implements TafResp {
+    private final TafResp delegate;
+    private final TaggedPrincipal principal;
+    private final String desc;
+    private float timing;
+
+    public TrustTafResp(final TafResp delegate, final TaggedPrincipal principal, final String desc) {
+        this.delegate = delegate;
+        this.principal = principal;
+        this.desc = desc + ' ' + delegate.desc();
+    }
+
+    @Override
+    public boolean isValid() {
+        return delegate.isValid();
+    }
+
+    @Override
+    public String desc() {
+        return desc;
+    }
+
+    @Override
+    public RESP isAuthenticated() {
+        return delegate.isAuthenticated();
+    }
+
+    @Override
+    public RESP authenticate() throws IOException {
+        return delegate.authenticate();
+    }
+
+    @Override
+    public TaggedPrincipal getPrincipal() {
+        return principal;
+    }
+
+    /* (non-Javadoc)
+     * @see org.onap.ccsdk.apps.cadi.taf.TafResp#getTarget()
+     */
+    @Override
+    public String getTarget() {
+        return delegate.getTarget();
+    }
+
+    @Override
+    public Access getAccess() {
+        return delegate.getAccess();
+    }
+
+    @Override
+    public boolean isFailedAttempt() {
+        return delegate.isFailedAttempt();
+    }
+    @Override
+    public float timing() {
+        return timing;
+    }
+
+    @Override
+    public void timing(long start) {
+        timing = Timing.millis(start);
+    }
+
+    public String toString() {
+        return principal.getName() + " by trust of " + desc();
+    }
+
+    @Override
+    public String taf() {
+        return "Trust";
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/basic/BasicHttpTaf.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/basic/BasicHttpTaf.java
new file mode 100644 (file)
index 0000000..54b98d8
--- /dev/null
@@ -0,0 +1,219 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf.basic;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Map;
+import java.util.TreeMap;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.Access.Level;
+import org.onap.ccsdk.apps.cadi.BasicCred;
+import org.onap.ccsdk.apps.cadi.CachedPrincipal;
+import org.onap.ccsdk.apps.cadi.CachedPrincipal.Resp;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.CredVal;
+import org.onap.ccsdk.apps.cadi.CredVal.Type;
+import org.onap.ccsdk.apps.cadi.CredValDomain;
+import org.onap.ccsdk.apps.cadi.Taf;
+import org.onap.ccsdk.apps.cadi.config.Config;
+import org.onap.ccsdk.apps.cadi.filter.MapBathConverter;
+import org.onap.ccsdk.apps.cadi.principal.BasicPrincipal;
+import org.onap.ccsdk.apps.cadi.principal.CachedBasicPrincipal;
+import org.onap.ccsdk.apps.cadi.taf.HttpTaf;
+import org.onap.ccsdk.apps.cadi.taf.TafResp;
+import org.onap.ccsdk.apps.cadi.taf.TafResp.RESP;
+import org.onap.ccsdk.apps.cadi.taf.dos.DenialOfServiceTaf;
+import org.onap.ccsdk.apps.cadi.util.CSV;
+
+/**
+ * BasicHttpTaf
+ *
+ * This TAF implements the "Basic Auth" protocol.
+ *
+ * WARNING! It is true for any implementation of "Basic Auth" that the password is passed unencrypted.
+ * This is because the expectation, when designed years ago, was that it would only be used in
+ * conjunction with SSL (https).  It is common, however, for users to ignore this on the assumption that
+ * their internal network is secure, or just ignorance.  Therefore, a WARNING will be printed
+ * when the HTTP Channel is not encrypted (unless explicitly turned off).
+ *
+ * @author Jonathan
+ *
+ */
+public class BasicHttpTaf implements HttpTaf {
+    private Access access;
+    private String realm;
+    private CredVal rbac;
+    private Map<String,CredVal> rbacs = new TreeMap<>();
+    private boolean warn;
+    private long timeToLive;
+    private MapBathConverter mapIds;
+
+    public BasicHttpTaf(Access access, CredVal rbac, String realm, long timeToLive, boolean turnOnWarning) {
+        this.access = access;
+        this.realm = realm;
+        this.rbac = rbac;
+        this.warn = turnOnWarning;
+        this.timeToLive = timeToLive;
+        String csvFile = access.getProperty(Config.CADI_BATH_CONVERT, null);
+        if(csvFile==null) {
+            mapIds=null;
+        } else {
+            try {
+                mapIds = new MapBathConverter(access, new CSV(access,csvFile));
+            } catch (IOException | CadiException e) {
+                access.log(e,"Bath Map Conversion is not initialzed (non fatal)");
+            }
+        }
+    }
+
+    public void add(final CredValDomain cvd) {
+        rbacs.put(cvd.domain(), cvd);
+    }
+
+    /**
+     * Note: BasicHttp works for either Carbon Based (Humans) or Silicon Based (machine) Lifeforms.
+     * @see Taf
+     */
+    public TafResp validate(Taf.LifeForm reading, HttpServletRequest req, HttpServletResponse resp) {
+        // See if Request implements BasicCred (aka CadiWrap or other), and if User/Pass has already been set separately
+        if (req instanceof BasicCred) {
+            BasicCred bc = (BasicCred)req;
+            if (bc.getUser()!=null) { // CadiWrap, if set, makes sure User & Password are both valid, or both null
+                if (DenialOfServiceTaf.isDeniedID(bc.getUser())!=null) {
+                    return DenialOfServiceTaf.respDenyID(access,bc.getUser());
+                }
+                CachedBasicPrincipal bp = new CachedBasicPrincipal(this,bc,realm,timeToLive);
+
+                // Be able to do Organizational specific lookups by Domain
+                CredVal cv = rbacs.get(bp.getDomain());
+                if (cv==null) {
+                    cv = rbac;
+                }
+
+                // ONLY FOR Last Ditch DEBUGGING...
+                // access.log(Level.WARN,bp.getName() + ":" + new String(bp.getCred()));
+                if (cv.validate(bp.getName(),Type.PASSWORD,bp.getCred(),req)) {
+                    return new BasicHttpTafResp(access,bp,bp.getName()+" authenticated by password",RESP.IS_AUTHENTICATED,resp,realm,false);
+                } else {
+                    //TODO may need timed retries in a given time period
+                    return new BasicHttpTafResp(access,bc.getUser(),buildMsg(bp,req,"user/pass combo invalid for ",bc.getUser(),"from",req.getRemoteAddr()),
+                            RESP.TRY_AUTHENTICATING,resp,realm,true);
+                }
+            }
+        }
+        // Get User/Password from Authorization Header value
+        String authz = req.getHeader("Authorization");
+        String target="unknown";
+
+        if (authz != null && authz.startsWith("Basic ")) {
+            if (warn&&!req.isSecure()) {
+                access.log(Level.WARN,"WARNING! BasicAuth has been used over an insecure channel");
+            }
+            if(mapIds != null) {
+                authz = mapIds.convert(access, authz);
+            }
+            try {
+                CachedBasicPrincipal ba = new CachedBasicPrincipal(this,authz,realm,timeToLive);
+                target=ba.getName();
+                if (DenialOfServiceTaf.isDeniedID(ba.getName())!=null) {
+                    return DenialOfServiceTaf.respDenyID(access,ba.getName());
+                }
+
+                final int at = ba.getName().indexOf('@');
+                CredVal cv = rbacs.get(ba.getName().substring(at+1));
+                if (cv==null) {
+                    cv = rbac; // default
+                }
+
+                // ONLY FOR Last Ditch DEBUGGING...
+                // access.log(Level.WARN,ba.getName() + ":" + new String(ba.getCred()));
+                if (cv.validate(ba.getName(), Type.PASSWORD, ba.getCred(), req)) {
+                    return new BasicHttpTafResp(access,ba, ba.getName()+" authenticated by BasicAuth password",RESP.IS_AUTHENTICATED,resp,realm,false);
+                } else {
+                    //TODO may need timed retries in a given time period
+                    return new BasicHttpTafResp(access,target,buildMsg(ba,req,"user/pass combo invalid"),
+                            RESP.TRY_AUTHENTICATING,resp,realm,true);
+                }
+            } catch (IOException e) {
+                String msg = buildMsg(null,req,"Failed HTTP Basic Authorization (", e.getMessage(), ')');
+                access.log(Level.INFO,msg);
+                return new BasicHttpTafResp(access,target,msg, RESP.TRY_AUTHENTICATING, resp, realm,true);
+            }
+        }
+        return new BasicHttpTafResp(access,target,"Requesting HTTP Basic Authorization",RESP.TRY_AUTHENTICATING,resp,realm,false);
+    }
+
+    protected String buildMsg(Principal pr, HttpServletRequest req, Object ... msg) {
+        StringBuilder sb = new StringBuilder();
+        if (pr!=null) {
+            sb.append("user=");
+            sb.append(pr.getName());
+            sb.append(',');
+        }
+        sb.append("ip=");
+        sb.append(req.getRemoteAddr());
+        sb.append(",port=");
+        sb.append(req.getRemotePort());
+        if (msg.length>0) {
+            sb.append(",msg=\"");
+            for (Object s : msg) {
+                sb.append(s.toString());
+            }
+            sb.append('"');
+        }
+        return sb.toString();
+    }
+
+    public void addCredVal(final String realm, final CredVal cv) {
+        rbacs.put(realm, cv);
+    }
+
+    public CredVal getCredVal(String key) {
+        CredVal cv = rbacs.get(key);
+        if (cv==null) {
+            cv = rbac;
+        }
+        return cv;
+    }
+
+    @Override
+    public Resp revalidate(CachedPrincipal prin, Object state) {
+        if (prin instanceof BasicPrincipal) {
+            BasicPrincipal ba = (BasicPrincipal)prin;
+            if (DenialOfServiceTaf.isDeniedID(ba.getName())!=null) {
+                return Resp.UNVALIDATED;
+            }
+            return rbac.validate(ba.getName(), Type.PASSWORD, ba.getCred(), state)?Resp.REVALIDATED:Resp.UNVALIDATED;
+        }
+        return Resp.NOT_MINE;
+    }
+
+    public String toString() {
+        return "Basic Auth enabled on realm: " + realm;
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/basic/BasicHttpTafResp.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/basic/BasicHttpTafResp.java
new file mode 100644 (file)
index 0000000..9fc8fc6
--- /dev/null
@@ -0,0 +1,69 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf.basic;
+
+import java.io.IOException;
+
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.principal.TaggedPrincipal;
+import org.onap.ccsdk.apps.cadi.taf.AbsTafResp;
+import org.onap.ccsdk.apps.cadi.taf.TafResp;
+
+public class BasicHttpTafResp extends AbsTafResp implements TafResp {
+    private static final String tafName = BasicHttpTaf.class.getSimpleName();
+    private HttpServletResponse httpResp;
+    private String realm;
+    private RESP status;
+    private final boolean wasFailed;
+
+    public BasicHttpTafResp(Access access, TaggedPrincipal principal, String description, RESP status, HttpServletResponse resp, String realm, boolean wasFailed) {
+        super(access, tafName, principal, description);
+        httpResp = resp;
+        this.realm = realm;
+        this.status = status;
+        this.wasFailed = wasFailed;
+    }
+
+    public BasicHttpTafResp(Access access, String target, String description, RESP status, HttpServletResponse resp, String realm, boolean wasFailed) {
+        super(access, tafName, target, description);
+        httpResp = resp;
+        this.realm = realm;
+        this.status = status;
+        this.wasFailed = wasFailed;
+    }
+
+    public RESP authenticate() throws IOException {
+        httpResp.setStatus(401); // Unauthorized
+        httpResp.setHeader("WWW-Authenticate", "Basic realm=\""+realm+'"');
+        return RESP.HTTP_REDIRECT_INVOKED;
+    }
+
+    public RESP isAuthenticated() {
+        return status;
+    }
+
+    public boolean isFailedAttempt() {
+        return wasFailed;
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/cert/CertIdentity.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/cert/CertIdentity.java
new file mode 100644 (file)
index 0000000..d60ee8c
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf.cert;
+
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+import org.onap.ccsdk.apps.cadi.principal.TaggedPrincipal;
+
+public interface CertIdentity {
+    /**
+     * identity from X509Certificate Object and/or certBytes
+     *
+     * If you have both, include them.  If you only have one, leave the other null, and it will be generated if needed
+     *
+     * The Request is there to obtain Header or Attribute info of ultimate user
+     *
+     * @param req
+     * @param cert
+     * @param certBytes
+     * @return
+     * @throws CertificateException
+     */
+    public TaggedPrincipal identity(HttpServletRequest req, X509Certificate cert, byte[] certBytes) throws CertificateException;
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/cert/X509HttpTafResp.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/cert/X509HttpTafResp.java
new file mode 100644 (file)
index 0000000..70fb578
--- /dev/null
@@ -0,0 +1,52 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf.cert;
+
+import java.io.IOException;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.principal.TaggedPrincipal;
+import org.onap.ccsdk.apps.cadi.taf.AbsTafResp;
+
+public class X509HttpTafResp extends AbsTafResp {
+    private static final String tafName = X509Taf.class.getSimpleName();
+
+    private RESP status;
+
+    public X509HttpTafResp(Access access, TaggedPrincipal principal, String description, RESP status) {
+        super(access, tafName, principal, description);
+         this.status = status;
+    }
+
+    public RESP authenticate() throws IOException {
+        return RESP.TRY_ANOTHER_TAF;
+    }
+
+    public RESP isAuthenticated() {
+        return status;
+    }
+
+    public String toString() {
+        return status.name();
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/cert/X509Taf.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/cert/X509Taf.java
new file mode 100644 (file)
index 0000000..c854523
--- /dev/null
@@ -0,0 +1,300 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf.cert;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.Signature;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+
+import javax.net.ssl.TrustManagerFactory;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.Access.Level;
+import org.onap.ccsdk.apps.cadi.CachedPrincipal;
+import org.onap.ccsdk.apps.cadi.CachedPrincipal.Resp;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.CredVal;
+import org.onap.ccsdk.apps.cadi.Lur;
+import org.onap.ccsdk.apps.cadi.Symm;
+import org.onap.ccsdk.apps.cadi.Taf.LifeForm;
+import org.onap.ccsdk.apps.cadi.config.Config;
+import org.onap.ccsdk.apps.cadi.config.SecurityInfo;
+import org.onap.ccsdk.apps.cadi.config.SecurityInfoC;
+import org.onap.ccsdk.apps.cadi.principal.TaggedPrincipal;
+import org.onap.ccsdk.apps.cadi.principal.X509Principal;
+import org.onap.ccsdk.apps.cadi.taf.HttpTaf;
+import org.onap.ccsdk.apps.cadi.taf.TafResp;
+import org.onap.ccsdk.apps.cadi.taf.TafResp.RESP;
+import org.onap.ccsdk.apps.cadi.taf.basic.BasicHttpTaf;
+import org.onap.ccsdk.apps.cadi.util.Split;
+
+public class X509Taf implements HttpTaf {
+    private static final String CERTIFICATE_NOT_VALID_FOR_AUTHENTICATION = "Certificate NOT valid for Authentication";
+    public static final CertificateFactory certFactory;
+    public static final MessageDigest messageDigest;
+    public static final TrustManagerFactory tmf;
+    private Access access;
+    private CertIdentity[] certIdents;
+//    private Lur lur;
+    private ArrayList<String> cadiIssuers;
+    private String env;
+    private SecurityInfo si;
+    private BasicHttpTaf bht;
+
+    static {
+        try {
+            certFactory = CertificateFactory.getInstance("X.509");
+            messageDigest = MessageDigest.getInstance("SHA-256"); // use this to clone
+            tmf = TrustManagerFactory.getInstance(SecurityInfoC.SSL_KEY_MANAGER_FACTORY_ALGORITHM);
+        } catch (Exception e) {
+            throw new RuntimeException("X.509 and SHA-256 are required for X509Taf",e);
+        }
+    }
+
+    public X509Taf(Access access, Lur lur, CertIdentity ... cis) throws CertificateException, NoSuchAlgorithmException, CadiException {
+        this.access = access;
+        env = access.getProperty(Config.AAF_ENV,null);
+        if (env==null) {
+            throw new CadiException("X509Taf requires Environment ("+Config.AAF_ENV+") to be set.");
+        }
+//        this.lur = lur;
+        this.cadiIssuers = new ArrayList<>();
+        for (String ci : access.getProperty(Config.CADI_X509_ISSUERS, "").split(":")) {
+            access.printf(Level.INIT, "Trusting Identity for Certificates signed by \"%s\"",ci);
+            cadiIssuers.add(ci);
+        }
+        try {
+            Class<?> dci = access.classLoader().loadClass("org.onap.ccsdk.apps.auth.direct.DirectCertIdentity");
+            if (dci==null) {
+                certIdents = cis;
+            } else {
+                CertIdentity temp[] = new CertIdentity[cis.length+1];
+                System.arraycopy(cis, 0, temp, 1, cis.length);
+                temp[0] = (CertIdentity) dci.newInstance();
+                certIdents=temp;
+            }
+        } catch (Exception e) {
+            certIdents = cis;
+        }
+
+        si = new SecurityInfo(access);
+    }
+
+    public static final X509Certificate getCert(byte[] certBytes) throws CertificateException {
+        ByteArrayInputStream bais = new ByteArrayInputStream(certBytes);
+        return (X509Certificate)certFactory.generateCertificate(bais);
+    }
+
+    public static final byte[] getFingerPrint(byte[] ba) {
+        MessageDigest md;
+        try {
+            md = (MessageDigest)messageDigest.clone();
+        } catch (CloneNotSupportedException e) {
+            // should never get here
+            return new byte[0];
+        }
+        md.update(ba);
+        return md.digest();
+    }
+
+    @Override
+    public TafResp validate(LifeForm reading, HttpServletRequest req, HttpServletResponse resp) {
+        // Check for Mutual SSL
+        try {
+            X509Certificate[] certarr = (X509Certificate[])req.getAttribute("javax.servlet.request.X509Certificate");
+            if (certarr!=null && certarr.length>0) {
+                si.checkClientTrusted(certarr);
+                // Note: If the Issuer is not in the TrustStore, it's not added to the Cert list
+                String issuer = certarr[0].getIssuerDN().toString();
+                String subject = certarr[0].getSubjectDN().getName();
+                access.printf(Level.DEBUG,"Client Certificate found\n  Subject '%s'\n  Issuer  '%s'",subject,issuer);
+                if (cadiIssuers.contains(issuer)) {
+                    // avoiding extra object creation, since this is validated EVERY transaction with a Cert
+                    int start = 0;
+                    int end = 1;
+                    int comma;
+                    int length = subject.length();
+
+                    compare:
+                    while(start<length) {
+                        while(Character.isWhitespace(subject.charAt(start))) {
+                            if(++start>length) {
+                                break compare;
+                            }
+                        }
+                        comma = subject.indexOf(',',start);
+                        if(comma<0) {
+                            end = subject.length();
+                        } else {
+                            end = comma<=0?0:comma-1;
+                        }
+                        while(Character.isWhitespace(subject.charAt(end))) {
+                            if(--end < 0) {
+                                break compare;
+                            }
+                        }
+                        if(subject.regionMatches(start, "OU=", 0, 3) ||
+                           subject.regionMatches(start, "CN=", 0, 3)) {
+                           int at = subject.indexOf('@', start);
+                           if(at<end && at>=0) {
+                               String[] sa = Split.splitTrim(':', subject, start+3,end+1);
+                               if (sa.length==1 || (sa.length>1 && env!=null && env.equals(sa[1]))) { // Check Environment
+                                   return new X509HttpTafResp(access,
+                                           new X509Principal(sa[0], certarr[0],(byte[])null,bht),
+                                           "X509Taf validated " + sa[0] + (sa.length<2?"":" for aaf_env " + env ), RESP.IS_AUTHENTICATED);
+                               } else {
+                                     access.printf(Level.DEBUG,"Certificate is not for environment '%s'",env);
+                                     break;
+                               }
+                           }
+                        }
+                        start = comma+1;
+                    }
+                     access.log(Level.DEBUG,"Certificate is not acceptable for Authentication");
+                } else {
+                    access.log(Level.DEBUG,"Issuer is not trusted for Authentication");
+                }
+            } else {
+                access.log(Level.DEBUG,"There is no client certificate on the transaction");
+            }
+
+
+            byte[] array = null;
+            byte[] certBytes = null;
+            X509Certificate cert=null;
+            String responseText=null;
+            String authHeader = req.getHeader("Authorization");
+
+            if (certarr!=null) {  // If cert !=null, Cert is Tested by Mutual Protocol.
+                if (authHeader!=null) { // This is only intended to be a Secure Connection, not an Identity
+                    for (String auth : Split.split(',',authHeader)) {
+                        if (auth.startsWith("Bearer ")) { // Bearer = OAuth... Don't use as Authenication
+                            return new X509HttpTafResp(access, null, "Certificate verified, but Bearer Token is presented", RESP.TRY_ANOTHER_TAF);
+                        }
+                    }
+                }
+                cert = certarr[0];
+                responseText = ", validated by Mutual SSL Protocol";
+            } else {         // If cert == null, Get Declared Cert (in header), but validate by having them sign something
+                if (authHeader != null) {
+                    for (String auth : Split.splitTrim(',',authHeader)) {
+                        if (auth.startsWith("x509 ")) {
+                            ByteArrayOutputStream baos = new ByteArrayOutputStream(auth.length());
+                            try {
+                                array = auth.getBytes();
+                                ByteArrayInputStream bais = new ByteArrayInputStream(array);
+                                Symm.base64noSplit.decode(bais, baos, 5);
+                                certBytes = baos.toByteArray();
+                                cert = getCert(certBytes);
+
+                                /**
+                                 * Identity from CERT if well know CA and specific encoded information
+                                 */
+                                // If found Identity doesn't work, try SignedStuff Protocol
+        //                                    cert.checkValidity();
+        //                                    cert.--- GET FINGERPRINT?
+                                String stuff = req.getHeader("Signature");
+                                if (stuff==null)
+                                    return new X509HttpTafResp(access, null, "Header entry 'Signature' required to validate One way X509 Certificate", RESP.TRY_ANOTHER_TAF);
+                                String data = req.getHeader("Data");
+        //                                    if (data==null)
+        //                                        return new X509HttpTafResp(access, null, "No signed Data to validate with X509 Certificate", RESP.TRY_ANOTHER_TAF);
+
+                                // Note: Data Pos shows is "<signatureType> <data>"
+        //                                    int dataPos = (stuff.indexOf(' ')); // determine what is Algorithm
+                                // Get Signature
+                                bais = new ByteArrayInputStream(stuff.getBytes());
+                                baos = new ByteArrayOutputStream(stuff.length());
+                                Symm.base64noSplit.decode(bais, baos);
+                                array = baos.toByteArray();
+        //                                    Signature sig = Signature.getInstance(stuff.substring(0, dataPos)); // get Algorithm from first part of Signature
+
+                                Signature sig = Signature.getInstance(cert.getSigAlgName());
+                                sig.initVerify(cert.getPublicKey());
+                                sig.update(data.getBytes());
+                                if (!sig.verify(array)) {
+                                    access.log(Level.ERROR, "Signature doesn't Match");
+                                    return new X509HttpTafResp(access, null, CERTIFICATE_NOT_VALID_FOR_AUTHENTICATION, RESP.TRY_ANOTHER_TAF);
+                                }
+                                responseText = ", validated by Signed Data";
+                            } catch (Exception e) {
+                                access.log(e, "Exception while validating Cert");
+                                return new X509HttpTafResp(access, null, CERTIFICATE_NOT_VALID_FOR_AUTHENTICATION, RESP.TRY_ANOTHER_TAF);
+                            }
+                        }
+                    }
+                }
+                if (cert==null) {
+                    return new X509HttpTafResp(access, null, "No Certificate Info on Transaction", RESP.TRY_ANOTHER_TAF);
+                }
+
+                // A cert has been found, match Identify
+                TaggedPrincipal prin=null;
+
+                for (int i=0;prin==null && i<certIdents.length;++i) {
+                    if ((prin=certIdents[i].identity(req, cert, certBytes))!=null) {
+                        responseText = prin.getName() + " matches Certificate " + cert.getSubjectX500Principal().getName() + responseText;
+                    }
+                }
+
+                // if Principal is found, check for "AS_USER" and whether this entity is trusted to declare
+                if (prin!=null) {
+                    // Note: Tag for Certs is Fingerprint, but that takes computation... leaving off
+                    return new X509HttpTafResp(
+                        access,
+                        prin,
+                        responseText,
+                        RESP.IS_AUTHENTICATED);
+                }
+            }
+        } catch (Exception e) {
+            return new X509HttpTafResp(access, null, e.getMessage(), RESP.TRY_ANOTHER_TAF);
+        }
+
+        return new X509HttpTafResp(access, null, "Certificate cannot be used for authentication", RESP.TRY_ANOTHER_TAF);
+    }
+
+    @Override
+    public Resp revalidate(CachedPrincipal prin, Object state) {
+        return null;
+    }
+
+    public void add(BasicHttpTaf bht) {
+        this.bht = bht;
+    }
+
+    public CredVal getCredVal(final String key) {
+        if (bht==null) {
+            return null;
+        } else {
+            return bht.getCredVal(key);
+        }
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/dos/DenialOfServiceTaf.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/dos/DenialOfServiceTaf.java
new file mode 100644 (file)
index 0000000..483334f
--- /dev/null
@@ -0,0 +1,375 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf.dos;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.CachedPrincipal;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.CachedPrincipal.Resp;
+import org.onap.ccsdk.apps.cadi.Taf.LifeForm;
+import org.onap.ccsdk.apps.cadi.config.Config;
+import org.onap.ccsdk.apps.cadi.taf.HttpTaf;
+import org.onap.ccsdk.apps.cadi.taf.PuntTafResp;
+import org.onap.ccsdk.apps.cadi.taf.TafResp;
+import org.onap.ccsdk.apps.cadi.taf.TafResp.RESP;
+
+public class DenialOfServiceTaf implements HttpTaf {
+    private static Map<String, Counter> deniedIP=null, deniedID=null;
+    private Access access;
+    private final TafResp puntNotDenied;
+    private static File dosIP, dosID;
+
+    /**
+     *
+     * @param hostname
+     * @param prod
+     * @throws CadiException
+     */
+    public DenialOfServiceTaf(Access access) throws CadiException {
+        puntNotDenied = new PuntTafResp("DenialOfServiceTaf", "This Transaction is not denied");
+        this.access = access;
+        if (dosIP==null || dosID == null) {
+            String dirStr;
+            if ((dirStr = access.getProperty(Config.AAF_DATA_DIR, null))!=null) {
+                dosIP = new File(dirStr+"/dosIP");
+                readIP();
+                dosID = new File(dirStr+"/dosID");
+                readID();
+            }
+        }
+    }
+
+    @Override
+    public TafResp validate(LifeForm reading, HttpServletRequest req, final HttpServletResponse resp) {
+        // Performance, when not needed
+        if (deniedIP != null) {
+            String ip;
+            Counter c = deniedIP.get(ip=req.getRemoteAddr());
+            if (c!=null) {
+                c.inc();
+                return respDenyIP(access,ip);
+            }
+        }
+
+        // Note:  Can't process Principal, because this is the first TAF, and no Principal is created.
+        // Other TAFs use "isDenied()" on this Object to validate.
+        return puntNotDenied;
+    }
+
+    @Override
+    public Resp revalidate(CachedPrincipal prin, Object state) {
+        // We always return NOT MINE, because DOS Taf does not ever validate
+        return Resp.NOT_MINE;
+    }
+
+    /*
+     *  for use in Other TAFs, before they attempt backend validation of
+     */
+    public static Counter isDeniedID(String identity) {
+        if (deniedID!=null) {
+            return deniedID.get(identity);
+        }
+        return null;
+    }
+
+    /**
+     *
+     */
+    public static Counter isDeniedIP(String ipvX) {
+        if (deniedIP!=null) {
+            return deniedIP.get(ipvX);
+        }
+        return null;
+    }
+
+    /**
+     * Return of "True" means IP has been added.
+     * Return of "False" means IP already added.
+     *
+     * @param ip
+     * @return
+     */
+    public static synchronized boolean denyIP(String ip) {
+        boolean rv = false;
+        if (deniedIP==null) {
+            deniedIP = new HashMap<>();
+            deniedIP.put(ip, new Counter(ip)); // Noted duplicated for minimum time spent
+            rv= true;
+        } else if (deniedIP.get(ip)==null) {
+            deniedIP.put(ip, new Counter(ip));
+            rv = true;
+        }
+        if (rv) {
+            writeIP();
+        }
+        return rv;
+    }
+
+    private static void writeIP() {
+        if (dosIP!=null && deniedIP!=null) {
+            if (deniedIP.isEmpty()) {
+                if (dosIP.exists()) {
+                    dosIP.delete();
+                }
+            } else {
+                PrintStream fos;
+                try {
+                    fos = new PrintStream(new FileOutputStream(dosIP,false));
+                    try {
+                        for (String ip: deniedIP.keySet()) {
+                            fos.println(ip);
+                        }
+                    } finally {
+                        fos.close();
+                    }
+                } catch (IOException e) {
+                    e.printStackTrace(System.err);
+                }
+            }
+        }
+    }
+
+    private static void readIP() {
+        if (dosIP!=null && dosIP.exists()) {
+            BufferedReader br;
+            try {
+                br = new BufferedReader(new FileReader(dosIP));
+                try {
+                    if (deniedIP==null) {
+                        deniedIP=new HashMap<>();
+                    }
+
+                    String line;
+                    while ((line=br.readLine())!=null) {
+                        deniedIP.put(line, new Counter(line));
+                    }
+                } finally {
+                    br.close();
+                }
+            } catch (IOException e) {
+                e.printStackTrace(System.err);
+            }
+        }
+    }
+
+
+    /**
+     * Return of "True" means IP has was removed.
+     * Return of "False" means IP wasn't being denied.
+     *
+     * @param ip
+     * @return
+     */
+    public static synchronized boolean removeDenyIP(String ip) {
+        if (deniedIP!=null && deniedIP.remove(ip)!=null) {
+            writeIP();
+            if (deniedIP.isEmpty()) {
+                deniedIP=null;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Return of "True" means ID has been added.
+     * Return of "False" means ID already added.
+     *
+     * @param ip
+     * @return
+     */
+    public static synchronized boolean denyID(String id) {
+        boolean rv = false;
+        if (deniedID==null) {
+            deniedID = new HashMap<>();
+            deniedID.put(id, new Counter(id)); // Noted duplicated for minimum time spent
+            rv = true;
+        } else if (deniedID.get(id)==null) {
+            deniedID.put(id, new Counter(id));
+            rv = true;
+        }
+        if (rv) {
+            writeID();
+        }
+        return rv;
+
+    }
+
+    private static void writeID() {
+        if (dosID!=null && deniedID!=null) {
+            if (deniedID.isEmpty()) {
+                if (dosID.exists()) {
+                    dosID.delete();
+                }
+            } else {
+                PrintStream fos;
+                try {
+                    fos = new PrintStream(new FileOutputStream(dosID,false));
+                    try {
+                        for (String ip: deniedID.keySet()) {
+                            fos.println(ip);
+                        }
+                    } finally {
+                        fos.close();
+                    }
+                } catch (IOException e) {
+                    e.printStackTrace(System.err);
+                }
+            }
+        }
+    }
+
+    private static void readID() {
+        if (dosID!=null && dosID.exists()) {
+            BufferedReader br;
+            try {
+                br = new BufferedReader(new FileReader(dosID));
+                try {
+                    if (deniedID==null) {
+                        deniedID=new HashMap<>();
+                    }
+
+                    String line;
+                    while ((line=br.readLine())!=null) {
+                        deniedID.put(line, new Counter(line));
+                    }
+                } finally {
+                    br.close();
+                }
+            } catch (IOException e) {
+                e.printStackTrace(System.err);
+            }
+        }
+    }
+
+    /**
+     * Return of "True" means ID has was removed.
+     * Return of "False" means ID wasn't being denied.
+     *
+     * @param ip
+     * @return
+     */
+    public static synchronized boolean removeDenyID(String id) {
+        if (deniedID!=null && deniedID.remove(id)!=null) {
+            writeID();
+            if (deniedID.isEmpty()) {
+                deniedID=null;
+            }
+
+            return true;
+        }
+        return false;
+    }
+
+    public List<String> report() {
+        int initSize = 0;
+        if (deniedIP!=null)initSize+=deniedIP.size();
+        if (deniedID!=null)initSize+=deniedID.size();
+        ArrayList<String> al = new ArrayList<>(initSize);
+        if (deniedID!=null) {
+            for (Counter c : deniedID.values()) {
+                al.add(c.toString());
+            }
+        }
+        if (deniedIP!=null) {
+            for (Counter c : deniedIP.values()) {
+                al.add(c.toString());
+            }
+        }
+        return al;
+    }
+
+    public static class Counter {
+        private final String name;
+        private int count = 0;
+        private Date first;
+        private long last; // note, we use "last" as long, to avoid popping useless dates on Heap.
+
+        public Counter(String name) {
+            this.name = name;
+            first = null;
+            last = 0L;
+            count = 0;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public int getCount() {
+            return count;
+        }
+
+        public long getLast() {
+            return last;
+        }
+
+        /*
+         * Only allow Denial of ServiceTaf to increment
+         */
+        private synchronized void inc() {
+            ++count;
+            last = System.currentTimeMillis();
+            if (first==null) {
+                first = new Date(last);
+            }
+        }
+
+        public String toString() {
+            if (count==0)
+                return name + " is on the denied list, but has not attempted Access";
+            else
+                return
+                    name +
+                    " has been denied " +
+                    count +
+                    " times since " +
+                    first +
+                    ".  Last denial was " +
+                    new Date(last);
+        }
+    }
+
+    public static TafResp respDenyID(Access access, String identity) {
+        return new DenialOfServiceTafResp(access, RESP.NO_FURTHER_PROCESSING, identity + " is on the Identity Denial list");
+    }
+
+    public static TafResp respDenyIP(Access access, String ip) {
+        return new DenialOfServiceTafResp(access, RESP.NO_FURTHER_PROCESSING, ip + " is on the IP Denial list");
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/dos/DenialOfServiceTafResp.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/taf/dos/DenialOfServiceTafResp.java
new file mode 100644 (file)
index 0000000..1ebb2e6
--- /dev/null
@@ -0,0 +1,55 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf.dos;
+
+import java.io.IOException;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.taf.AbsTafResp;
+
+public class DenialOfServiceTafResp extends AbsTafResp  {
+    private static final String tafName = DenialOfServiceTaf.class.getSimpleName();
+
+    private RESP ect;  // Homage to Arethra Franklin
+
+    public DenialOfServiceTafResp(Access access, RESP resp, String description ) {
+        super(access, tafName, "dos", description);
+        ect = resp;
+    }
+
+    // Override base behavior of checking Principal and trying another TAF
+    @Override
+    public RESP isAuthenticated() {
+        return ect;
+    }
+
+
+    public RESP authenticate() throws IOException {
+        return ect;
+    }
+
+    @Override
+    public String taf() {
+        return "DOS";
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/CSV.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/CSV.java
new file mode 100644 (file)
index 0000000..89eb70c
--- /dev/null
@@ -0,0 +1,290 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ */
+
+package org.onap.ccsdk.apps.cadi.util;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.Access.Level;
+import org.onap.ccsdk.apps.cadi.CadiException;
+
+/**
+ * Read CSV file for various purposes
+ *
+ * @author Instrumental(Jonathan)
+ *
+ */
+public class CSV {
+    private File csv;
+    private Access access;
+    private boolean processAll;
+    private char delimiter = ',';
+    private boolean go;
+
+    public CSV(Access access, File file) {
+        this.access = access;
+        csv = file;
+        processAll = false;
+        go = true;
+    }
+
+    public CSV(Access access, String csvFilename) {
+        this.access = access;
+        csv = new File(csvFilename);
+        processAll = false;
+        go = true;
+    }
+
+    public CSV setDelimiter(char delimiter) {
+        this.delimiter = delimiter;
+        return this;
+    }
+
+    public String name() {
+        return csv.getName();
+    }
+
+    public CSV processAll() {
+        processAll = true;
+        return this;
+    }
+    /*
+     * Create your code to accept the List<String> row.
+     *
+     * Your code may keep the List... CSV does not hold onto it.
+     *
+     * @author Instrumental(Jonathan)
+     *
+     */
+    public interface Visitor {
+        void visit(List<String> row) throws IOException, CadiException;
+    }
+
+    public void visit(Visitor visitor) throws IOException, CadiException {
+        BufferedReader br = new BufferedReader(new FileReader(csv));
+        try {
+            String line;
+            StringBuilder sb = new StringBuilder();
+            while(go && (line = br.readLine())!=null) {
+                line=line.trim();
+                if(!line.startsWith("#") && line.length()>0) {
+//                    System.out.println(line);  uncomment to debug
+                    List<String> row = new ArrayList<>();
+                    boolean quotes=false;
+                    boolean escape=false;
+                    char c = 0;
+                    for(int i=0;i<line.length();++i) {
+                        switch(c=line.charAt(i)) {
+                            case '"':
+                                if(quotes) {
+                                    if(i<line.length()-1) { // may look ahead
+                                        if('"' == line.charAt(i+1)) {
+                                            sb.append(c);
+                                            ++i;
+                                        } else {
+                                            quotes = false;
+                                        }
+                                    } else {
+                                        quotes=false;
+                                    }
+                                } else {
+                                    quotes=true;
+                                }
+                                break;
+                            case '\\':
+                                if(escape) {
+                                    sb.append(c);
+                                    escape = false;
+                                } else {
+                                    escape = true;
+                                }
+                                break;
+                            case 'n':
+                                if(escape) {
+                                    sb.append("\\n");
+                                    escape=false;
+                                } else {
+                                    sb.append(c);
+                                }
+                                break;
+                            default:
+                                if(delimiter==c) {
+                                    if(quotes) {
+                                        sb.append(c);
+                                    } else {
+                                        row.add(sb.toString());
+                                        sb.setLength(0);
+                                    }
+                                } else {
+                                    sb.append(c);
+                                }
+                        }
+                    }
+                    if(sb.length()>0 || c==',') {
+                        row.add(sb.toString());
+                        sb.setLength(0);
+                    }
+                    try {
+                        visitor.visit(row);
+                    } catch (CadiException e) {
+                        if(processAll) {
+                            access.log(Level.ERROR,e);
+                        } else {
+                            throw e;
+                        }
+                    }
+                }
+            }
+        } finally {
+            br.close();
+        }
+    }
+
+    public Writer writer() throws FileNotFoundException {
+        return new Writer(false);
+    }
+
+    public Writer writer(boolean append) throws FileNotFoundException {
+        return new Writer(append);
+    }
+
+    public interface RowSetter {
+        public void row(Object ... objs);
+    }
+
+    public static class Saver implements RowSetter {
+        List<String> ls= new ArrayList<>();
+
+        @Override
+        public void row(Object ... objs) {
+            if(objs.length>0) {
+                for(Object o : objs) {
+                    if(o != null) {
+                        if(o instanceof String[]) {
+                            for(String str : (String[])o) {
+                                ls.add(str);
+                            }
+                        } else {
+                            ls.add(o.toString());
+                        }
+                    }
+                }
+            }
+        }
+
+        public List<String> asList() {
+            List<String> rv = ls;
+            ls = new ArrayList<>();
+            return rv;
+        }
+    }
+
+    public class Writer implements RowSetter {
+        private PrintStream ps;
+        private Writer(final boolean append) throws FileNotFoundException {
+            ps = new PrintStream(new FileOutputStream(csv,append));
+        }
+
+        @Override
+        public void row(Object ... objs) {
+            if(objs.length>0) {
+                boolean first = true;
+                for(Object o : objs) {
+                    if(first) {
+                        first = false;
+                    } else {
+                        ps.append(delimiter);
+                    }
+                    if(o == null) {
+                    } else if(o instanceof String[]) {
+                        for(String str : (String[])o) {
+                            print(str);
+                        }
+                    } else {
+                        print(o.toString());
+                    }
+                }
+                ps.println();
+            }
+        }
+
+        private void print(String s) {
+            boolean quote = s.matches(".*[,|\"].*");
+            if(quote) {
+                ps.append('"');
+                ps.print(s.replace("\"", "\"\"")
+                          .replace("'", "''")
+                          .replace("\\", "\\\\"));
+                ps.append('"');
+            } else {
+                ps.append(s);
+            }
+
+
+        }
+        /**
+         * Note: CSV files do not actually support Comments as a standard, but it is useful
+         * @param comment
+         */
+        public void comment(String comment, Object ... objs) {
+            ps.print("# ");
+            ps.printf(comment,objs);
+            ps.println();
+        }
+
+        public void flush() {
+            ps.flush();
+        }
+
+        public void close() {
+            flush();
+            ps.close();
+        }
+
+        public String toString() {
+            return csv.getAbsolutePath();
+        }
+    }
+
+    /**
+     * Provides a way to stop processing records from inside a Visit
+     */
+    public void stop() {
+        go = false;
+    }
+
+    public void delete() {
+        csv.delete();
+    }
+
+    public String toString() {
+        return csv.getAbsolutePath();
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/Chmod.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/Chmod.java
new file mode 100644 (file)
index 0000000..de029bb
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.util;
+
+import java.io.File;
+import java.io.IOException;
+
+public interface Chmod {
+    public void chmod(File f) throws IOException;
+
+    public static final Chmod to755 = new Chmod() {
+        public void chmod(File f) throws IOException {
+            f.setExecutable(true, false);
+            f.setExecutable(true, true);
+            f.setReadable(true, false);
+            f.setReadable(true, true);
+            f.setWritable(false, false);
+            f.setWritable(true, true);
+        }
+    };
+
+    public static final Chmod to644 = new Chmod() {
+        public void chmod(File f) throws IOException {
+            f.setExecutable(false, false);
+            f.setExecutable(false, true);
+            f.setReadable(true, false);
+            f.setReadable(true, true);
+            f.setWritable(false, false);
+            f.setWritable(true, true);
+        }
+    };
+
+    public static final Chmod to400 = new Chmod() {
+        public void chmod(File f) throws IOException {
+            f.setExecutable(false, false);
+            f.setExecutable(false, true);
+            f.setReadable(false, false);
+            f.setReadable(true, true);
+            f.setWritable(false, false);
+            f.setWritable(false, true);
+        }
+    };
+}
\ No newline at end of file
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/FQI.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/FQI.java
new file mode 100644 (file)
index 0000000..66b59b1
--- /dev/null
@@ -0,0 +1,51 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.util;
+
+public class FQI {
+    /**
+     * Take a Fully Qualified User, and get a Namespace from it.
+     * @param fqi
+     * @return
+     */
+    public final static String reverseDomain(final String fqi) {
+        StringBuilder sb = null;
+        String[] split = Split.split('.',fqi);
+        int at;
+        for (int i=split.length-1;i>=0;--i) {
+            if (sb == null) {
+                sb = new StringBuilder();
+            } else {
+                sb.append('.');
+            }
+
+            if ((at = split[i].indexOf('@'))>0) {
+                sb.append(split[i].subSequence(at+1, split[i].length()));
+            } else {
+                sb.append(split[i]);
+            }
+        }
+
+        return sb==null?"":sb.toString();
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/FixURIinfo.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/FixURIinfo.java
new file mode 100644 (file)
index 0000000..2dbc7c9
--- /dev/null
@@ -0,0 +1,75 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ */
+
+package org.onap.ccsdk.apps.cadi.util;
+
+import java.net.URI;
+
+/**
+ * URI and URL, if the host does not have "dots", will interpret Host:port as Authority
+ *
+ * This is very problematic for Containers, which like single name entries.
+ * @author Instrumental(Jonathan)
+ *
+ */
+public class FixURIinfo {
+    private String auth;
+    private String host;
+    private int port;
+
+    public FixURIinfo(URI uri) {
+        auth = uri.getAuthority();
+        host = uri.getHost();
+        if(host==null || (auth!=null && auth.startsWith(host))) {
+            if(auth!=null) {
+                int colon = auth.indexOf(':');
+                if(colon >= 0 ) {
+                    host = auth.substring(0, colon);
+                    port = Integer.parseInt(auth.substring(colon+1));
+                } else {
+                    host = auth;
+                    port = uri.getPort();
+                    if (port < 1) {
+                        if ("http".equals(uri.getScheme())) {
+                            port = 80;
+                        } else if ("https".equals(uri.getScheme())) {
+                            port = 443;
+                        } else {
+                            throw new RuntimeException ("Invalid scheme provided for URI " + uri);
+                        }
+                    }
+                }
+                auth=null;
+            }
+        }
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public String getUserInfo() {
+        return auth;
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/Holder.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/Holder.java
new file mode 100644 (file)
index 0000000..dd31317
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.util;
+
+/**
+ * Use to set Variables outside of Anonymous classes.
+ * @author Jonathan
+ *
+ * @param <T>
+ */
+public class Holder<T> {
+    private T value;
+    public Holder(T t) {
+        value = t;
+    }
+    public T set(T t) {
+        value = t;
+        return t;
+    }
+
+    public T get() {
+        return value;
+    }
+    public String toString() {
+        return value.toString();
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/JsonOutputStream.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/JsonOutputStream.java
new file mode 100644 (file)
index 0000000..933877d
--- /dev/null
@@ -0,0 +1,89 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class JsonOutputStream extends OutputStream {
+    private static final byte[] TWO_SPACE = "  ".getBytes();
+    private OutputStream os;
+    private boolean closeable;
+    private int indent = 0;
+    private int prev,ret=0;
+
+    public JsonOutputStream(OutputStream os) {
+        // Don't close these, or dire consequences.
+        closeable = !os.equals(System.out) && !os.equals(System.err);
+        this.os = os;
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+        if (ret=='\n') {
+            ret = 0;
+            if (prev!=',' || (b!='{' && b!='[')) {
+                os.write('\n');
+                for (int i=0;i<indent;++i) {
+                    os.write(TWO_SPACE);
+                }
+            }
+        }
+        switch(b) {
+            case '{':
+            case '[':
+                    ret = '\n';
+                    ++indent;
+                    break;
+            case '}':
+            case ']':
+                    --indent;
+                    os.write('\n');
+                    for (int i=0;i<indent;++i) {
+                        os.write(TWO_SPACE);
+                    }
+                    break;
+            case ',':
+                    ret = '\n';
+                    break;
+
+        }
+        os.write(b);
+        prev = b;
+    }
+    public void resetIndent() {
+        indent = 1;
+    }
+
+    @Override
+    public void flush() throws IOException {
+        os.flush();
+    }
+
+    @Override
+    public void close() throws IOException {
+        if (closeable) {
+            os.close();
+        }
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/Log.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/Log.java
new file mode 100644 (file)
index 0000000..ab75d0c
--- /dev/null
@@ -0,0 +1,37 @@
+/**
+ * ============LICENSE_START====================================================
+ * Log
+ * ===========================================================================
+ * Copyright (c) May 11, 2020 Gathman Systems. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ */
+package org.onap.ccsdk.apps.cadi.util;
+
+/**
+ * A basic log interface used to Facade into Log Libraries used locally.
+ * 
+ * @author Jonathan
+ *
+ */
+public interface Log {
+       enum Type {debug,info,warn,error,trace};
+    public void log(Log.Type type, Object ... o);
+
+    public final static Log NULL = new Log() {
+        @Override
+        public void log(Log.Type type, Object ... o) {
+        }
+    };
+}
\ No newline at end of file
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/MaskFormatException.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/MaskFormatException.java
new file mode 100644 (file)
index 0000000..0f0f15a
--- /dev/null
@@ -0,0 +1,31 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.util;
+
+@SuppressWarnings("serial")
+public class MaskFormatException extends Exception {
+
+    public MaskFormatException(String string) {
+            super(string);
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/MyConsole.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/MyConsole.java
new file mode 100644 (file)
index 0000000..2ab28e1
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.util;
+
+public interface MyConsole {
+    public String readLine(String fmt, Object ... args);
+    public char[] readPassword(String fmt, Object ... args);
+    public void printf(String fmt, Object ...args);
+}
\ No newline at end of file
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/NetMask.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/NetMask.java
new file mode 100644 (file)
index 0000000..3446e25
--- /dev/null
@@ -0,0 +1,99 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.util;
+
+/*
+ * NetMask - a class to quickly validate whether a given IP is part of a mask, as defined by bytes or standard String format.
+ *
+ * Needs the IPV6 Mask Builder.
+ */
+public class NetMask {
+    private long mask;
+
+    public NetMask(byte[] inBytes) {
+        mask = derive(inBytes);
+    }
+
+    public NetMask(String string) throws MaskFormatException {
+        mask = derive(string,true);
+    }
+
+    public boolean isInNet(byte[] inBytes) {
+        long addr = derive(inBytes);
+        return (mask & addr) == addr;
+    }
+
+    public boolean isInNet(String str) {
+        long addr;
+        try {
+            addr = derive(str,false);
+            return (mask & addr) == addr;
+        } catch (MaskFormatException e) {
+            // will not hit this code;
+            return false;
+        }
+    }
+
+    public static long derive(byte[] inBytes) {
+        long addr = 0L;
+        int offset = inBytes.length*8;
+        for (int i=0;i<inBytes.length;++i) {
+            addr&=(inBytes[i]<<offset);
+            offset-=8;
+        }
+        return addr;
+    }
+
+    public static long derive(String str, boolean check) throws MaskFormatException {
+        long rv=0L;
+        int idx=str.indexOf(':');
+        int slash = str.indexOf('/');
+
+        if (idx<0) { // Not IPV6, so it's IPV4... Is there a mask of 123/254?
+            idx=str.indexOf('.');
+            int offset = 24;
+            int end = slash>=0?slash:str.length();
+            int bits = slash>=0?Integer.parseInt(str.substring(slash+1)):32;
+            if (check && bits>32) {
+                throw new MaskFormatException("Invalid Mask Offset in IPV4 Address");
+            }
+            int prev = 0;
+            long lbyte;
+            while (prev<end) {
+                if (idx<0) {
+                    idx = end;
+                }
+                lbyte = Long.parseLong(str.substring(prev, idx));
+                if (check && (lbyte>255 || lbyte<0)) {
+                    throw new MaskFormatException("Invalid Byte in IPV4 Address");
+                }
+                rv|=lbyte<<offset;
+                prev = ++idx;
+                idx=str.indexOf('.',prev);
+                offset-=8;
+            }
+            rv|=0x00000000FFFFFFFFL>>bits;
+        }
+        return rv;
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/Pool.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/Pool.java
new file mode 100644 (file)
index 0000000..419be8c
--- /dev/null
@@ -0,0 +1,453 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+/*
+ * Pool
+ *
+ * Author: Jonathan
+ * 5/27/2011
+ */
+package org.onap.ccsdk.apps.cadi.util;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import org.onap.ccsdk.apps.cadi.CadiException;
+
+/**
+ * This Class pools on an As-Needed-Basis any particular kind of class, which is
+ * quite suitable for expensive operations.
+ *
+ * The user calls "get" on a Pool, and if a waiting resource (T) is available,
+ * it will be returned. Otherwise, one will be created with the "Creator" class
+ * (must be defined for (T)).
+ *
+ * You can Prime the instances to avoid huge startup costs
+ *
+ * The returned "Pooled" object simply has to call "done()" and the object is
+ * returned to the pool. If the developer does not return the object, a memory
+ * leak does not occur. There are no references to the object once "get" is
+ * called. However, the developer who does not return the object when done
+ * obviates the point of the pool, as new Objects are created in place of the
+ * Object not returned when another call to "get" is made.
+ *
+ * There is a cushion of extra objects, currently defaulted to MAX_RANGE. If the
+ * items returned become higher than the MAX_RANGE, the object is allowed to go
+ * out of scope, and be cleaned up. the default can be changed on a per-pool
+ * basis.
+ *
+ * Class revamped for CadiExceptions and Access logging 10/4/2017
+ *
+ * @author Jonathan
+ *
+ * @param <T>
+ */
+public class Pool<T> {
+    /**
+     * This is a constant which specified the default maximum number of unused
+     * objects to be held at any given time.
+     */
+    public static final int MAX_RANGE = 6; // safety
+    
+    /**
+     * Maximum objects, in use or waiting
+     */
+    public static final int MAX_OBJECTS = 20; // assumption for thread
+
+    /**
+     * only Simple List needed.
+     *
+     * NOTE TO MAINTAINERS: THIS OBJECT DOES IT'S OWN SYNCHRONIZATION. All
+     * changes that touch list must account for correctly synchronizing list.
+     */
+    private LinkedList<Pooled<T>> list;
+
+    /**
+     * keep track of how many elements are currently available to use, to avoid asking list.
+     */
+    private int count;
+    
+    /**
+     * how many objects have been asked for, but not returned or tossed
+     */
+    private int used;
+    
+    /**
+     * Actual MAX number of spares allowed to hang around. Can be set to
+     * something besides the default MAX_RANGE.
+     */
+    private int max_range = MAX_RANGE;
+
+    /**
+     * Actual MAX number of Objects both in use, or waiting.
+     * This does not actually affect the Pool, because the objects, once they leave the pool, are not known until
+     * they are put back with done (offer).  It only affects the "overLimit()" function.
+     * 
+     * Important... this information is only valid if PooledObjects call "done()" or "toss()".
+     */
+    private int max_objects = MAX_OBJECTS;
+    
+    /**
+     * The Creator for this particular pool. It must work for type T.
+     */
+    private Creator<T> creator;
+
+    private Log logger;
+
+    /**
+     * Create a new Pool, given the implementation of Creator<T>, which must be
+     * able to create/destroy T objects at will.
+     *
+     * @param creator
+     */
+    public Pool(Creator<T> creator) {
+        count = used = 0;
+        this.creator = creator;
+        list = new LinkedList<>();
+        logger = Log.NULL;
+    }
+
+    /**
+     * Attach Pool Logging activities to any other Logging Mechanism.
+     * @param logger
+     */
+    public void setLogger(Log logger) {
+        this.logger = logger;
+        // Also reset existing Pooled objects
+        for(Pooled<?> p : list) {
+            if(p.content instanceof LogAware) {
+               ((LogAware)p.content).setLog(logger);
+            } else {
+               break;
+            }
+        }
+    }
+
+    public void log(Log.Type type, Object ...objects) {
+        logger.log(type,objects);
+    }
+
+    /**
+     * Preallocate a certain number of T Objects. Useful for services so that
+     * the first transactions don't get hit with all the Object creation costs
+     * 
+     * It is assumed that priming also means that it is the minimum desired available resources.  Therefore, 
+     * max_range is set to prime, if less than current max_range, if it is default.
+     * 
+     * @param lt
+     * @param prime
+     * @throws CadiException
+     */
+    public Pool<T> prime(int prime) throws CadiException  {
+       if(max_range == MAX_RANGE && prime<max_range) {
+               max_range = prime;
+       }
+        for (int i = 0; i < prime; ++i) {
+            Pooled<T> pt = new Pooled<T>(creator.create(), this);
+            synchronized (list) {
+                list.addFirst(pt);
+                ++count;
+                ++used;
+            }
+        }
+        return this;
+    }
+
+    /**
+     * Destroy and remove all remaining objects. This is valuable for closing
+     * down all Allocated objects cleanly for exiting. It is also a good method
+     * for removing objects when, for instance, all Objects are invalid because
+     * of broken connections, etc.
+     * 
+     * Use in conjunction with setMaxRange to no longer store objects, i.e.
+     *  
+     *  pool.setMaxRange(0).drain();
+     */
+    public synchronized void drain() {
+       while(list.size()>0) {
+            Pooled<T> pt = list.remove();
+            --used;
+            String name = pt.content.toString();
+            creator.destroy(pt.content);
+            logger.log(Log.Type.debug,"Pool destroyed", name);
+       }
+        count = 0;
+    }
+    
+    /**
+     * This is the essential function for Pool. Get an Object "T" inside a
+     * "Pooled<T>" object. If there is a spare Object, then use it. If not, then
+     * create and pass back.
+     *
+     * This one uses a Null LogTarget
+     *
+     * IMPORTANT: When the use of this object is done (and the object is still
+     * in a valid state), then "done()" should be called immediately to allow
+     * the object to be reused. That is the point of the Pool...
+     *
+     * If the Object is in an invalid state, then "toss()" should be used so the
+     * Pool doesn't pass on invalid objects to others.
+     *
+     * @param lt
+     * @return
+     * @throws CadiException
+     */
+    public Pooled<T> get() throws CadiException {
+        Pooled<T> pt;
+        synchronized (list) {
+               pt = list.pollLast();
+        }
+        if (pt == null) {
+            pt = new Pooled<T>(creator.create(), this);
+            ++used;
+        } else {
+            --count;
+            creator.reuse(pt.content);
+        }
+        return pt;
+    }
+
+    /**
+     * This function will validate whether the Objects are still in a usable
+     * state. If not, they are tossed from the Pool. This is valuable to have
+     * when Remote Connections go down, and there is a question on whether the
+     * Pooled Objects are still functional.
+     *
+     * @return
+     */
+    public boolean validate() {
+        boolean rv = true;
+        synchronized (list) {
+            for (Iterator<Pooled<T>> iter = list.iterator(); iter.hasNext();) {
+                Pooled<T> t = iter.next();
+                if (!creator.isValid(t.content)) {
+                    rv = false;
+                    t.toss();
+                    iter.remove();
+                }
+            }
+        }
+        return rv;
+    }
+
+    /**
+     * This is an internal method, used only by the Internal Pooled<T> class.
+     *
+     * The Pooled<T> class "offers" it's Object back after use. It is an
+     * "offer", because Pool will simply destroy and remove the object if it has
+     * more than enough spares.
+     *
+     * @param lt
+     * @param used
+     * @return
+     */
+    // Used only by Pooled<T>
+    private boolean offer(Pooled<T> usedP) {
+        if (count < max_range) {
+            synchronized (list) {
+                list.addFirst(usedP);
+                ++count;
+            }
+            logger.log(Log.Type.trace,"Pool recovered ", creator);
+        } else {
+               destroy(usedP.content);
+        }
+        return false;
+    }
+    
+    /**
+     * Destroy, using Creator's specific semantics, the Object, and decrement "used"
+     * 
+     * @param t
+     */
+    private void destroy(T t) {
+        creator.destroy(t);
+        synchronized (list) {
+               --used;
+        }
+        logger.log(Log.Type.debug,"Pool destroyed ", creator);
+    }
+
+    /**
+     * The Creator Interface give the Pool the ability to Create, Destroy and
+     * Validate the Objects it is maintaining. Thus, it is a specially written
+     * Implementation for each type.
+     *
+     * @author Jonathan
+     *
+     * @param <T>
+     */
+    public interface Creator<T> {
+        public T create() throws CadiException;
+
+        public void destroy(T t);
+
+        public boolean isValid(T t);
+
+        public void reuse(T t);
+    }
+
+    /**
+     * Pooled Classes can be "Log Aware", which means they can tie into the same
+     * Logging element that the Pool is using.  To do this, the Object must implement "LogAware"
+     * 
+     * @author Jonathan
+     *
+     */
+    public interface LogAware {
+       public void setLog(Log log);
+    }
+
+    /**
+     * The "Pooled<T>" class is the transient class that wraps the actual Object
+     * T for API use/ It gives the ability to return ("done()", or "toss()") the
+     * Object to the Pool when processing is finished.
+     *
+     * For Safety, i.e. to avoid memory leaks and invalid Object States, there
+     * is a "finalize" method. It is strictly for when coder forgets to return
+     * the object, or perhaps hasn't covered the case during Exceptions or
+     * Runtime Exceptions with finally (preferred). This should not be
+     * considered normal procedure, as finalize() is called at an undetermined
+     * time during garbage collection, and is thus rather useless for a Pool.
+     * However, we don't want Coding Mistakes to put the whole program in an
+     * invalid state, so if something happened such that "done()" or "toss()"
+     * were not called, the resource is still cleaned up as well as possible.
+     *
+     * @author Jonathan
+     *
+     * @param <T>
+     */
+    public static class Pooled<T> {
+        public final T content;
+        private Pool<T> pool;
+
+        /**
+         * Create the Wrapping Object Pooled<T>.
+         *
+         * @param t
+         * @param pool
+         * @param logTarget
+         */
+        public Pooled(T t, Pool<T> pool) {
+            content = t;
+            if(t instanceof LogAware) {
+               ((LogAware)t).setLog(pool.logger);
+            }
+            this.pool = pool;
+        }
+
+        /**
+         * This is the key API for the Pool, as calling "done()" offers this
+         * object back to the Pool for reuse.
+         *
+         * Do not use the Pooled<T> object again after calling "done()".
+         */
+        public void done() {
+            if (pool != null) {
+                pool.offer(this);
+            }
+        }
+
+        /**
+         * The user of the Object may discover that the Object t is no longer in
+         * a valid state. Don't put Garbage back in the Refrigerator... Toss it,
+         * if it's no longer valid.
+         *
+         * toss() is also used for draining the Pool, etc.
+         *
+         * toss() will attempt to destroy the Object by using the Creator
+         * Interface.
+         *
+         */
+        public void toss() {
+            if (pool != null) {
+                pool.destroy(content);
+            }
+            // Don't allow finalize to put it back in.
+            pool = null;
+        }
+
+        /**
+         * Just in case someone neglected to offer back object... Do not rely on
+         * this, as there is no specific time when finalize is called, which
+         * rather defeats the purpose of a Pool.
+         */
+        @Override
+        protected void finalize() throws Throwable {
+            if (pool != null) {
+                done();
+                pool = null;
+            }
+        }
+        
+        @Override
+        public String toString() {
+               return content.toString();
+        }
+    }
+
+    /**
+     * Set a Max Range for numbers of spare objects waiting to be used.
+     *
+     * No negative numbers are allowed
+     * 
+     * Use in conjunction with drain to no longer store objects, i.e.
+     *  
+     *  pool.setMaxRange(0).drain();
+     *
+     * @return
+     */
+    public Pool<T> setMaxRange(int max_range) {
+        // Do not allow negative numbers
+        this.max_range = Math.max(0, max_range);
+        return this;
+    }
+    
+    /**
+     * Set a Max Range for numbers of spare objects waiting to be used.
+     *
+     * No negative numbers are allowed
+     *
+     * @return
+     */
+    public Pool<T> setMaxObjects(int max_objects) {
+        // Do not allow negative numbers
+        this.max_objects = Math.max(0, max_objects);
+        return this;
+    }
+
+    /**
+     * return whether objects in use or waiting are beyond max allowed
+     * 
+     * Pool does not actually stop new creations, but allows this to be used by
+     * other entities to limit number of creations of expensive Objects, like 
+     * Thread Pooling
+     *
+     */
+    public boolean tooManyObjects() {
+       return used > max_objects;
+    }
+
+    public String toString() {
+       return String.format("Pool: count(%d), used(%d), max_range(%d), max_objects(%d)",
+                       count, used,max_range,max_objects);
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/Split.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/Split.java
new file mode 100644 (file)
index 0000000..a5f6578
--- /dev/null
@@ -0,0 +1,123 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.util;
+
+/**
+ * Split by Char, optional Trim
+ *
+ * Note: Copied from Inno to avoid linking issues.
+ * Note: I read the String split and Pattern split code, and we can do this more efficiently for a single Character
+ *
+ * 8/20/2015
+ */
+
+public class Split {
+    private static final String[] EMPTY = new String[0];
+
+    public static String[] split(char c, String value) {
+        if (value==null) {
+            return EMPTY;
+        }
+
+        return split(c,value,0,value.length());
+    }
+
+    public static String[] split(char c, String value, int start, int end) {
+        if (value==null) {
+            return EMPTY;
+        }
+
+        // Count items to preallocate Array (memory alloc is more expensive than counting twice)
+        int count,idx;
+        for (count=1,idx=value.indexOf(c,start);idx>=0 && idx<end;idx=value.indexOf(c,++idx),++count);
+        String[] rv = new String[count];
+        if (count==1) {
+            rv[0]=value.substring(start,end);
+        } else {
+            int last=0;
+            count=-1;
+            for (idx=value.indexOf(c,start);idx>=0 && idx<end;idx=value.indexOf(c,idx)) {
+                rv[++count]=value.substring(last,idx);
+                last = ++idx;
+            }
+            rv[++count]=value.substring(last,end);
+        }
+        return rv;
+    }
+
+    public static String[] splitTrim(char c, String value, int start, int end) {
+        if (value==null) {
+            return EMPTY;
+        }
+
+        // Count items to preallocate Array (memory alloc is more expensive than counting twice)
+        int count,idx;
+        for (count=1,idx=value.indexOf(c,start);idx>=0 && idx<end;idx=value.indexOf(c,++idx),++count);
+        String[] rv = new String[count];
+        if (count==1) {
+            rv[0]=value.substring(start,end).trim();
+        } else {
+            int last=start;
+            count=-1;
+            for (idx=value.indexOf(c,start);idx>=0 && idx<end;idx=value.indexOf(c,idx)) {
+                rv[++count]=value.substring(last,idx).trim();
+                last = ++idx;
+            }
+            rv[++count]=value.substring(last,end).trim();
+        }
+        return rv;
+    }
+
+    public static String[] splitTrim(char c, String value) {
+        if (value==null) {
+            return EMPTY;
+        }
+        return splitTrim(c,value,0,value.length());
+    }
+
+    public static String[] splitTrim(char c, String value, int size) {
+        if (value==null) {
+            return EMPTY;
+        }
+
+        int idx;
+        String[] rv = new String[size];
+        if (size==1) {
+            rv[0]=value.trim();
+        } else {
+            int last=0;
+            int count=-1;
+            size-=2;
+            for (idx=value.indexOf(c);idx>=0 && count<size;idx=value.indexOf(c,idx)) {
+                rv[++count]=value.substring(last,idx).trim();
+                last = ++idx;
+            }
+            if (idx>0) {
+                rv[++count]=value.substring(last,idx).trim();
+            } else {
+                rv[++count]=value.substring(last).trim();
+            }
+        }
+        return rv;
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/SubStandardConsole.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/SubStandardConsole.java
new file mode 100644 (file)
index 0000000..25876eb
--- /dev/null
@@ -0,0 +1,70 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.util;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+// Substandard, because System.in doesn't do Passwords..
+public class SubStandardConsole implements MyConsole {
+    private final static char[] BLANK = new char[0];
+    private final BufferedReader br;
+
+    public SubStandardConsole() {
+        br = new BufferedReader(new InputStreamReader(System.in));
+    }
+
+    @Override
+    public String readLine(String fmt, Object... args) {
+        String rv;
+        try {
+            System.out.printf(fmt,args);
+            rv = br.readLine();
+            if (args.length==1 && rv.length()==0) {
+                rv = args[0].toString();
+            }
+        } catch (IOException e) {
+            System.err.println("uh oh...");
+            rv = "";
+        }
+        return rv;
+    }
+
+    @Override
+    public char[] readPassword(String fmt, Object... args) {
+        try {
+            System.out.printf(fmt,args);
+            String response = br.readLine();
+            return response==null?BLANK:response.toCharArray();
+
+        } catch (IOException e) {
+            System.err.println("uh oh...");
+            return BLANK;
+        }
+    }
+
+    @Override
+    public void printf(String fmt, Object... args) {
+        System.out.printf(fmt, args);
+    }
+}
\ No newline at end of file
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/TheConsole.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/TheConsole.java
new file mode 100644 (file)
index 0000000..1734760
--- /dev/null
@@ -0,0 +1,47 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.util;
+
+public class TheConsole implements MyConsole {
+    @Override
+    public String readLine(String fmt, Object... args) {
+        String rv = System.console().readLine(fmt, args);
+        if (args.length>0 && args[0]!=null && rv.length()==0) {
+            rv = args[0].toString();
+        }
+        return rv;
+    }
+
+    @Override
+    public char[] readPassword(String fmt, Object... args) {
+        return System.console().readPassword(fmt, args);
+    }
+
+    public static boolean implemented() {
+        return System.console()!=null;
+    }
+
+    @Override
+    public void printf(String fmt, Object... args) {
+        System.console().printf(fmt, args);
+    }
+}
\ No newline at end of file
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/Timing.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/Timing.java
new file mode 100644 (file)
index 0000000..69db666
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+package org.onap.ccsdk.apps.cadi.util;
+
+public class Timing {
+    public static float millis(final long start) {
+        return (System.nanoTime() - start) / 1000000f;
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/UserChainManip.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/UserChainManip.java
new file mode 100644 (file)
index 0000000..13ddfe0
--- /dev/null
@@ -0,0 +1,77 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.util;
+
+import org.onap.ccsdk.apps.cadi.UserChain;
+
+public class UserChainManip {
+    /**
+        Build an element in the correct format for UserChain.
+        Format:<APP>:<ID>:<protocol>[:AS][,<APP>:<ID>:<protocol>]*
+        @see UserChain
+    */
+    public static StringBuilder build(StringBuilder sb, String app, String id, UserChain.Protocol proto, boolean as) {
+        boolean mayAs;
+        if (!(mayAs=sb.length()==0)) {
+            sb.append(',');
+        }
+        sb.append(app);
+        sb.append(':');
+        sb.append(id);
+        sb.append(':');
+        sb.append(proto.name());
+        if (as && mayAs) {
+            sb.append(":AS");
+        }
+        return sb;
+    }
+
+    public static String idToNS(String id) {
+        if (id==null) {
+            return "";
+        } else {
+            StringBuilder sb = new StringBuilder();
+            char c;
+            int end;
+            boolean first = true;
+            for (int idx = end = id.length()-1;idx>=0;--idx) {
+                if ((c = id.charAt(idx))=='@' || c=='.')  {
+                    if (idx<end) {
+                        if (first) {
+                            first = false;
+                        } else {
+                            sb.append('.');
+                        }
+                        for (int i=idx+1;i<=end;++i) {
+                            sb.append(id.charAt(i));
+                        }
+                    }
+                    end=idx-1;
+                    if (c=='@') {
+                        break;
+                    }
+                }
+            }
+            return sb.toString();
+        }
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/Vars.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/util/Vars.java
new file mode 100644 (file)
index 0000000..0609efd
--- /dev/null
@@ -0,0 +1,120 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.util;
+
+import java.util.List;
+
+public class Vars {
+    /**
+     * Simplified Conversion based on typical use of getting AT&T style RESTful Error Messages
+     * @param text
+     * @param vars
+     * @return
+     */
+    public static String convert(final String text, final List<String> vars) {
+        StringBuilder sb = new StringBuilder();
+        Object[] array = new Object[vars.size()];
+        convert(sb,text,vars.toArray(array));
+        return sb.toString();
+    }
+    /**
+     * Convert a format string with "%s" into AT&T RESTful Error %1 %2 (number) format
+     * If "holder" is passed in, it is built with full Message extracted (typically for Logging)
+     * @param holder
+     * @param text
+     * @param vars
+     * @return
+     */
+    public static String convert(final StringBuilder holder, final String text, final Object ... vars) {
+        StringBuilder sb = null;
+        int idx,index=0,prev = 0;
+
+        if (text.contains("%s")) {
+            sb = new StringBuilder();
+        }
+
+        StringBuilder[] sbs = new StringBuilder[] {sb,holder};
+        boolean replace, clearIndex = false;
+        int c;
+        while ((idx=text.indexOf('%',prev))>=0) {
+            replace = false;
+            if (clearIndex) {
+                index=0;
+            }
+            if (sb!=null) {
+                sb.append(text,prev,idx);
+            }
+            if (holder!=null) {
+                holder.append(text,prev,idx);
+            }
+
+            boolean go = true;
+            while (go) {
+                if (text.length()>++idx) {
+                    switch(c=text.charAt(idx)) {
+                        case '0': case '1': case '2': case '3': case '4':
+                        case '5': case '6': case '7': case '8': case '9':
+                            index *=10;
+                            index +=(c-'0');
+                            clearIndex=replace=true;
+                            continue;
+                        case 's':
+                            ++index;
+                            replace = true;
+                            continue;
+                        default:
+                            break;
+                    }
+                }
+                prev = idx;
+                go=false;
+                if (replace) {
+                    if (sb!=null) {
+                        sb.append('%');
+                        sb.append(index);
+                    }
+                    if (index<=vars.length) {
+                        if (holder!=null) {
+                            holder.append(vars[index-1]);
+                        }
+                    }
+                } else {
+                    for (StringBuilder s : sbs) {
+                        if (s!=null) {
+                            s.append("%");
+                        }
+                    }
+                }
+            }
+        }
+
+        if (sb!=null) {
+            sb.append(text,prev,text.length());
+        }
+        if (holder!=null) {
+            holder.append(text,prev,text.length());
+        }
+
+        return sb==null?text:sb.toString();
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/wsse/Action.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/wsse/Action.java
new file mode 100644 (file)
index 0000000..879a140
--- /dev/null
@@ -0,0 +1,37 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.wsse;
+
+/**
+ * Interface to specify an action deep within a parsing tree on a local object
+ *
+ * We use a Generic so as to be flexible on create what that object actually is.  This is passed in at the
+ * root "parse" call of Match.  Similar to a "Visitor" Pattern, this object is passed upon reaching the right
+ * point in a parse tree.
+ *
+ * @author Jonathan
+ *
+ * @param <OUTPUT>
+ */
+interface Action<OUTPUT> {
+    public boolean content(OUTPUT output, String text);
+}
\ No newline at end of file
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/wsse/Match.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/wsse/Match.java
new file mode 100644 (file)
index 0000000..bd80c21
--- /dev/null
@@ -0,0 +1,130 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.wsse;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.events.XMLEvent;
+
+/**
+ * Match Class allows you to build an automatic Tree of StAX (or StAX like)
+ * Objects for frequent use.
+ *
+ * OBJECT is a type which you which to do some end Actions on, similar to a Visitor pattern, see Action
+ *
+ * Note: We have implemented with XReader and XEvent, rather than StAX for performance reasons.
+ *
+ * @see Action
+ * @see Match
+ * @see XEvent
+ * @see XReader
+ *
+ * @author Jonathan
+ *
+ * @param <OUTPUT>
+ */
+//@SuppressWarnings("restriction")
+public class Match<OUTPUT> {
+    private QName qname;
+    private Match<OUTPUT>[] next;
+    private Match<OUTPUT> prev;
+    private Action<OUTPUT> action = null;
+    private boolean stopAfter;
+    private boolean exclusive;
+
+
+    @SafeVarargs
+    public Match(String ns, String name, Match<OUTPUT> ... next) {
+        this.qname = new QName(ns,name);
+        this.next = next;
+        stopAfter = exclusive = false;
+        for (Match<OUTPUT> m : next) { // add the possible tags to look for
+            if (!m.stopAfter)m.prev = this;
+        }
+    }
+
+    public Match<OUTPUT> onMatch(OUTPUT output, XReader reader) throws XMLStreamException {
+        while (reader.hasNext()) {
+            XEvent event = reader.nextEvent();
+            switch(event.getEventType()) {
+                case XMLEvent.START_ELEMENT:
+                    QName e_qname = event.asStartElement().getName();
+                    //System.out.println("Start - " + e_qname);
+                    boolean match = false;
+                    for (Match<OUTPUT> m : next) {
+                        if (e_qname.equals(m.qname)) {
+                            match=true;
+                            if (m.onMatch(output, reader)==null) {
+                                return null; // short circuit Parsing
+                            }
+                            break;
+                        }
+                    }
+                    if (exclusive && !match) // When Tag MUST be present, i.e. the Root Tag, versus info we're not interested in
+                        return null;
+                    break;
+                case XMLEvent.CHARACTERS:
+                    //System.out.println("Data - " +event.asCharacters().getData());
+                    if (action!=null) {
+                        if (!action.content(output,event.asCharacters().getData())) {
+                            return null;
+                        }
+                    }
+                    break;
+                case XMLEvent.END_ELEMENT:
+                    //System.out.println("End - " + event.asEndElement().getName());
+                    if (event.asEndElement().getName().equals(qname)) {
+                        return prev;
+                    }
+                    break;
+                case XMLEvent.END_DOCUMENT:
+                    return null; // Exit Chain
+            }
+        }
+        return this;
+    }
+
+    /**
+     * When this Matched Tag has completed, Stop parsing and end
+     * @return
+     */
+    public Match<OUTPUT> stopAfter() {
+        stopAfter = true;
+        return this;
+    }
+
+    /**
+     * Mark that this Object MUST be matched at this level or stop parsing and end
+     *
+     * @param action
+     * @return
+     */
+    public Match<OUTPUT> exclusive() {
+        exclusive = true;
+        return this;
+    }
+
+    public Match<OUTPUT> set(Action<OUTPUT> action) {
+        this.action = action;
+        return this;
+    }
+}
\ No newline at end of file
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/wsse/WSSEParser.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/wsse/WSSEParser.java
new file mode 100644 (file)
index 0000000..4f85fa5
--- /dev/null
@@ -0,0 +1,83 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.wsse;
+
+import java.io.InputStream;
+
+import javax.xml.stream.XMLStreamException;
+
+import org.onap.ccsdk.apps.cadi.BasicCred;
+
+
+/**
+ * WSSE Parser
+ *
+ * Read the User and Password from WSSE Formatted SOAP Messages
+ *
+ * This class uses StAX so that processing is stopped as soon as the Security User/Password are read into BasicCred, or the Header Ends
+ *
+ * This class is intended to be created once (or very few times) and reused as much as possible.
+ *
+ * It is as thread safe as StAX parsing is.
+ *
+ * @author Jonathan
+ */
+public class WSSEParser {
+    private static final String SOAP_NS = "http://schemas.xmlsoap.org/soap/envelope/";
+    private static final String WSSE_NS = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
+    private Match<BasicCred> parseTree;
+
+    public WSSEParser() {
+        // soap:Envelope/soap:Header/wsse:Security/wsse:UsernameToken/[wsse:Password&wsse:Username]
+        parseTree = new Match<BasicCred>(SOAP_NS,"root", // need a root level to start from... Doesn't matter what the tag is
+            new Match<BasicCred>(SOAP_NS,"Envelope",
+                new Match<BasicCred>(SOAP_NS,"Header",
+                    new Match<BasicCred>(WSSE_NS,"Security",
+                        new Match<BasicCred>(WSSE_NS,"UsernameToken",
+                            new Match<BasicCred>(WSSE_NS,"Password").set(new Action<BasicCred>() {
+                                public boolean content(BasicCred bc,String text) {
+                                    bc.setCred(text.getBytes());
+                                    return true;
+                                }
+                            }),
+                            new Match<BasicCred>(WSSE_NS,"Username").set(new Action<BasicCred>() {
+                                public boolean content(BasicCred bc,String text) {
+                                    bc.setUser(text);
+                                    return true;
+                                }
+                            })
+                        ).stopAfter() // if found, end when UsernameToken ends (no further processing needed)
+                    )
+                ).stopAfter() // Stop Processing when Header Ends
+            ).exclusive()// Envelope must match Header, and no other.  FYI, Body comes after Header short circuits (see above), so it's ok
+        ).exclusive(); // root must be Envelope
+    }
+
+    public XMLStreamException parse(BasicCred bc, InputStream is) {
+        try {
+            parseTree.onMatch(bc, new XReader(is));
+            return null;
+        } catch (XMLStreamException e) {
+            return e;
+        }
+    }
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/wsse/XEvent.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/wsse/XEvent.java
new file mode 100644 (file)
index 0000000..909dd1d
--- /dev/null
@@ -0,0 +1,135 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.wsse;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.events.XMLEvent;
+
+/**
+ * XEvent
+ *
+ * This mechanism mimics a minimal portion of StAX "XMLEvent", enough to work with minimal XReader.
+ *
+ * We implement the same interface, as much as minimally necessary, as XMLEvent for these small usages so as to
+ * be interchangeable in the future, if so desired
+ *
+ * @author Jonathan
+ *
+ */
+// @SuppressWarnings("restriction")
+public abstract class XEvent {
+
+    public abstract int getEventType();
+
+    public StartElement asStartElement() {
+        return (StartElement)this;
+    }
+
+    public Characters asCharacters() {
+        return (Characters)this;
+    }
+
+    public EndElement asEndElement() {
+        return (EndElement)this;
+    }
+
+    public static abstract class NamedXEvent extends XEvent {
+        private QName qname;
+
+        public NamedXEvent(QName qname) {
+            this.qname = qname;
+        }
+
+        public QName getName() {
+            return qname;
+        }
+    }
+    public static class StartElement extends NamedXEvent {
+
+        public StartElement(String ns, String tag) {
+            super(new QName(ns,tag));
+        }
+
+        @Override
+        public int getEventType() {
+            return XMLEvent.START_ELEMENT;
+        }
+    }
+
+    public static class EndElement extends NamedXEvent {
+        public EndElement(String ns, String tag) {
+            super(new QName(ns,tag));
+        }
+
+        @Override
+        public int getEventType() {
+            return XMLEvent.END_ELEMENT;
+        }
+    }
+
+    public static class Characters extends XEvent {
+        private String data;
+
+        public Characters(String data) {
+            this.data = data;
+        }
+        @Override
+        public int getEventType() {
+            return XMLEvent.CHARACTERS;
+        }
+
+        public String getData() {
+            return data;
+        }
+    }
+
+    public static class StartDocument extends XEvent {
+
+        @Override
+        public int getEventType() {
+            return XMLEvent.START_DOCUMENT;
+        }
+
+    }
+
+    public static class EndDocument extends XEvent {
+
+        @Override
+        public int getEventType() {
+            return XMLEvent.END_DOCUMENT;
+        }
+
+    }
+    public static class Comment extends XEvent {
+        public final String value;
+        public Comment(String value) {
+            this.value = value;
+        }
+
+        @Override
+        public int getEventType() {
+            return XMLEvent.COMMENT;
+        }
+
+    }
+
+}
diff --git a/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/wsse/XReader.java b/cadi/core/src/main/java/org/onap/ccsdk/apps/cadi/wsse/XReader.java
new file mode 100644 (file)
index 0000000..3da8edd
--- /dev/null
@@ -0,0 +1,427 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.wsse;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+
+import javax.xml.stream.XMLStreamException;
+
+/**
+ * XReader
+ * This class works similarly as StAX, except StAX has more behavior than is needed.  That would be ok, but
+ * StAX also was Buffering in their code in such as way as to read most if not all the incoming stream into memory,
+ * defeating the purpose of pre-reading only the Header
+ *
+ * This Reader does no back-tracking, but is able to create events based on syntax and given state only, leaving the
+ * Read-ahead mode of the InputStream up to the other classes.
+ *
+ * At this time, we only implement the important events, though if this is good enough, it could be expanded, perhaps to
+ * replace the original XMLReader from StAX.
+ *
+ * @author Jonathan
+ *
+ */
+// @SuppressWarnings("restriction")
+public class XReader {
+    private XEvent curr,another;
+    private InputStream is;
+    private ByteArrayOutputStream baos;
+    private int state, count, last;
+
+    private Stack<Map<String,String>> nsses;
+
+    public XReader(InputStream is) {
+        this.is = is;
+        curr = another = null;
+        baos = new ByteArrayOutputStream();
+        state = BEGIN_DOC;
+        count = 0;
+        nsses = new Stack<Map<String,String>>();
+    }
+
+    public boolean hasNext() throws XMLStreamException {
+        if (curr==null) {
+            curr = parse();
+        }
+        return curr!=null;
+    }
+
+    public XEvent nextEvent() {
+        XEvent xe = curr;
+        curr = null;
+        return xe;
+    }
+
+    //
+    // State Flags
+    //
+    // Note: The State of parsing XML can be complicated.  There are too many to cleanly keep in "booleans".  Additionally,
+    // there are certain checks that can be better made with Bitwise operations within switches
+    // Keeping track of state this way also helps us to accomplish logic without storing any back characters except one
+    private final static int BEGIN_DOC=  0x000001;
+    private final static int DOC_TYPE=   0x000002;
+    private final static int QUESTION_F= 0x000004;
+    private final static int QUESTION =  0x000008;
+    private final static int START_TAG = 0x000010;
+    private final static int END_TAG =      0x000020;
+    private final static int VALUE=         0x000040;
+    private final static int COMMENT =   0x001000;
+    private final static int COMMENT_E = 0x002000;
+    private final static int COMMENT_D1 =0x010000;
+    private final static int COMMENT_D2 =0x020000;
+    private final static int COMMENT_D3 =0x040000;
+    private final static int COMMENT_D4 =0x080000;
+    // useful combined Comment states
+    private final static int IN_COMMENT=COMMENT|COMMENT_E|COMMENT_D1|COMMENT_D2;
+    private final static int COMPLETE_COMMENT = COMMENT|COMMENT_E|COMMENT_D1|COMMENT_D2|COMMENT_D3|COMMENT_D4;
+
+
+    private XEvent parse() throws XMLStreamException {
+        Map<String,String> nss = nsses.isEmpty()?null:nsses.peek();
+
+        XEvent rv;
+        if ((rv=another)!=null) { // "another" is a tag that may have needed to be created, but not
+                                 // immediately returned.  Save for next parse.  If necessary, this could be turned into
+                                 // a FIFO storage, but a single reference is enough for now.
+            another = null;      // "rv" is now set for the Event, and will be returned.  Set to Null.
+        } else {
+            boolean go = true;
+            int c=0;
+
+            try {
+                while (go && (c=is.read())>=0) {
+                    ++count;
+                    switch(c) {
+                        case '<': // Tag is opening
+                            state|=~BEGIN_DOC; // remove BEGIN_DOC flag, this is possibly an XML Doc
+                            XEvent cxe = null;
+                            if (baos.size()>0) { // If there are any characters between tags, we send as Character Event
+                                String chars = baos.toString().trim();  // Trim out WhiteSpace before and after
+                                if (chars.length()>0) { // don't send if Characters were only whitespace
+                                    cxe = new XEvent.Characters(chars);
+                                    baos.reset();
+                                    go = false;
+                                }
+                            }
+                            last = c;  // make sure "last" character is set for use in "ParseTag"
+                            Tag t = parseTag(); // call subroutine to process the tag as a unit
+                            String ns;
+                            switch(t.state&(START_TAG|END_TAG)) {
+                                case START_TAG:
+                                        nss = getNss(nss,t);             // Only Start Tags might have NS Attributes
+                                                                        // Get any NameSpace elements from tag.  If there are, nss will become
+                                                                        // a new Map with all the previous NSs plus the new.  This provides
+                                                                        // scoping behavior when used with the Stack
+                                    // drop through on purpose
+                                case END_TAG:
+                                    ns = t.prefix==null||nss==null?"":nss.get(t.prefix); // Get the namespace from prefix (if exists)
+                                    break;
+                                default:
+                                    ns = "";
+                            }
+                            if (ns==null)
+                                throw new XMLStreamException("Invalid Namespace Prefix at " + count);
+                            go = false;
+                            switch(t.state) { // based on
+                              case DOC_TYPE:
+                                  rv = new XEvent.StartDocument();
+                                  break;
+                              case COMMENT:
+                                  rv = new XEvent.Comment(t.value);
+                                  break;
+                              case START_TAG:
+                                  rv = new XEvent.StartElement(ns,t.name);
+                                  nsses.push(nss);                // Change potential scope for Namespace
+                                  break;
+                              case END_TAG:
+                                  rv = new XEvent.EndElement(ns,t.name);
+                                  nss = nsses.pop();            // End potential scope for Namespace
+                                  break;
+                              case START_TAG|END_TAG:            // This tag is both start/end  aka <myTag/>
+                                  rv = new XEvent.StartElement(ns,t.name);
+                                  if (last=='/')another = new XEvent.EndElement(ns,t.name);
+                            }
+                            if (cxe!=null) {     // if there is a Character Event, it actually should go first.  ow.
+                                another = rv;   // Make current Event the "another" or next event, and
+                                rv = cxe;        // send Character Event now
+                            }
+                            break;
+                        case ' ':
+                        case '\t':
+                        case '\n':
+                            if ((state&BEGIN_DOC)==BEGIN_DOC) { // if Whitespace before doc, just ignore
+                                break;
+                            }
+                            // fallthrough on purpose
+                        default:
+                            if ((state&BEGIN_DOC)==BEGIN_DOC) { // if there is any data at the start other than XML Tag, it's not XML
+                                throw new XMLStreamException("Parse Error: This is not an XML Doc");
+                            }
+                            baos.write(c); // save off Characters
+                    }
+                    last = c; // Some processing needs to know what the last character was, aka Escaped characters... ex \"
+                }
+            } catch (IOException e) {
+                throw new XMLStreamException(e); // all errors parsing will be treated as XMLStreamErrors (like StAX)
+            }
+            if (c==-1 && (state&BEGIN_DOC)==BEGIN_DOC) {                // Normally, end of stream is ok, however, we need to know if the
+                throw new XMLStreamException("Premature End of File"); // document isn't an XML document, so we throw exception if it
+            }                                                           // hasn't yet been determined to be an XML Doc
+        }
+        return rv;
+    }
+
+    /**
+     * parseTag
+     *
+     * Parsing a Tag is somewhat complicated, so it's helpful to separate this process from the
+     * higher level Parsing effort
+     * @return
+     * @throws IOException
+     * @throws XMLStreamException
+     */
+    private Tag parseTag() throws IOException, XMLStreamException {
+        Tag tag = null;
+        boolean go = true;
+        state = 0;
+        int c, quote=0; // If "quote" is 0, then we're not in a quote.  We set ' (in pretag) or " in attribs accordingly to denote quoted
+        String prefix=null,name=null,value=null;
+        baos.reset();
+
+        while (go && (c=is.read())>=0) {
+            ++count;
+            if (quote!=0) { // If we're in a quote, we only end if we hit another quote of the same time, not preceded by \
+                if (c==quote && last!='\\') {
+                    quote=0;
+                } else {
+                    baos.write(c);
+                }
+            } else if ((state&COMMENT)==COMMENT) { // similar to Quote is being in a comment
+                switch(c) {
+                    case '-':
+                        switch(state) { // XML has a complicated Quote set... <!-- --> ... we keep track if each has been met with flags.
+                            case COMMENT|COMMENT_E:
+                                state|=COMMENT_D1;
+                                break;
+                            case COMMENT|COMMENT_E|COMMENT_D1:
+                                state|=COMMENT_D2;
+                                baos.reset();                // clear out "!--", it's a Comment
+                                break;
+                            case COMMENT|COMMENT_E|COMMENT_D1|COMMENT_D2:
+                                state|=COMMENT_D3;
+                                baos.write(c);
+                                break;
+                            case COMMENT|COMMENT_E|COMMENT_D1|COMMENT_D2|COMMENT_D3:
+                                state|=COMMENT_D4;
+                                baos.write(c);
+                                break;
+                        }
+                        break;
+                    case '>': // Tag indicator has been found, do we have all the comment characters in line?
+                        if ((state&COMPLETE_COMMENT)==COMPLETE_COMMENT) {
+                            byte ba[] = baos.toByteArray();
+                            tag = new Tag(null,null, new String(ba,0,ba.length-2));
+                            baos.reset();
+                            go = false;
+                            break;
+                        }
+                        // fall through on purpose
+                    default:
+                        state&=~(COMMENT_D3|COMMENT_D4);
+                        if ((state&IN_COMMENT)!=IN_COMMENT) state&=~IN_COMMENT; // false alarm, it's not actually a comment
+                        baos.write(c);
+                }
+            } else { // Normal Tag Processing loop
+                switch(c) {
+                    case '?':
+                        switch(state & (QUESTION_F|QUESTION)) {  // Validate the state of Doc tag... <?xml ... ?>
+                            case QUESTION_F:
+                                state |= DOC_TYPE;
+                                state &= ~QUESTION_F;
+                                break;
+                            case 0:
+                                state |=QUESTION_F;
+                                break;
+                            default:
+                                throw new IOException("Bad character [?] at " + count);
+                        }
+                        break;
+                    case '!':
+                        if (last=='<') {
+                            state|=COMMENT|COMMENT_E; // likely a comment, continue processing in Comment Loop
+                        }
+                        baos.write(c);
+                        break;
+                    case '/':
+                        state|=(last=='<'?END_TAG:(END_TAG|START_TAG));  // end tag indicator </xxx>, ,or both <xxx/>
+                        break;
+                    case ':':
+                        prefix=baos.toString(); // prefix indicator
+                        baos.reset();
+                        break;
+                    case '=':                    // used in Attributes
+                        name=baos.toString();
+                        baos.reset();
+                        state|=VALUE;
+                        break;
+                    case '>': // end the tag, which causes end of this subprocess as well as formulation of the found data
+                        go = false;
+                        // passthrough on purpose
+                    case ' ':
+                    case '\t':
+                    case '\n': // white space indicates change in internal tag state, ex between name and between attributes
+                        if ((state&VALUE)==VALUE) {
+                            value = baos.toString();    // we're in VALUE state, add characters to Value
+                        } else if (name==null) {
+                            name = baos.toString();        // we're in Name state (default) add characters to Name
+                        }
+                        baos.reset();                    // we've assigned chars, reset buffer
+                        if (name!=null) {                // Name is not null, there's a tag in the offing here...
+                            Tag t = new Tag(prefix,name,value);
+                            if (tag==null) {                // Set as the tag to return, if not exists
+                                tag = t;
+                            } else {                    // if we already have a Tag, then we'll treat this one as an attribute
+                                tag.add(t);
+                            }
+                        }
+                        prefix=name=value=null;            // reset these values in case we loop for attributes.
+                        break;
+                    case '\'':                            // is the character one of two kinds of quote?
+                    case '"':
+                        if (last!='\\') {
+                            quote=c;
+                            break;
+                        }
+                        // Fallthrough ok
+                    default:
+                        baos.write(c);                    // write any unprocessed bytes into buffer
+
+                }
+            }
+            last = c;
+        }
+        int type = state&(DOC_TYPE|COMMENT|END_TAG|START_TAG); // get just the Tag states and turn into Type for Tag
+        if (type==0) {
+            type=START_TAG;
+        }
+        if (tag!=null) {
+            tag.state|=type;    // add the appropriate Tag States
+        }
+        return tag;
+    }
+
+    /**
+     * getNSS
+     *
+     * If the tag contains some Namespace attributes, create a new nss from the passed in one, copy all into it, then add
+     * This provides Scoping behavior
+     *
+     * if Nss is null in the first place, create an new nss, so we don't have to deal with null Maps.
+     *
+     * @param nss
+     * @param t
+     * @return
+     */
+    private Map<String, String> getNss(Map<String, String> nss, Tag t) {
+        Map<String,String> newnss = null;
+        if (t.attribs!=null) {
+            for (Tag tag : t.attribs) {
+                if ("xmlns".equals(tag.prefix)) {
+                    if (newnss==null) {
+                        newnss = new HashMap<>();
+                        if (nss!=null)newnss.putAll(nss);
+                    }
+                    newnss.put(tag.name, tag.value);
+                }
+            }
+        }
+        //return newnss==null?(nss==null?new HashMap<String,String>():nss):newnss;
+        if (newnss==null) {
+            if (nss==null) {
+                newnss = new HashMap<>();
+            } else {
+                newnss = nss;
+            }
+        }
+        return newnss;
+    }
+
+    /**
+     * The result of the parseTag method
+     *
+     * Data is split up into prefix, name and value portions. "Tags" with Values that are inside a Tag are known in XLM
+     * as Attributes.
+     *
+     * @author Jonathan
+     *
+     */
+    public class Tag {
+        public int state;
+        public String prefix,name,value;
+        public List<Tag> attribs;
+
+        public Tag(String prefix, String name, String value) {
+            this.prefix = prefix;
+            this.name = name;
+            this.value = value;
+            attribs = null;
+        }
+
+        /**
+         * add an attribute
+         * Not all tags need attributes... lazy instantiate to save time and memory
+         * @param tag
+         */
+        public void add(Tag attrib) {
+            if (attribs == null) {
+                attribs = new ArrayList<>();
+            }
+            attribs.add(attrib);
+        }
+
+        public String toString() {
+            StringBuffer sb = new StringBuffer();
+            if (prefix!=null) {
+                sb.append(prefix);
+                sb.append(':');
+            }
+            sb.append(name==null?"!!ERROR!!":name);
+
+            char quote = ((state&DOC_TYPE)==DOC_TYPE)?'\'':'"';
+            if (value!=null) {
+                sb.append('=');
+                sb.append(quote);
+                sb.append(value);
+                sb.append(quote);
+            }
+            return sb.toString();
+        }
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/config/test/JU_Get.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/config/test/JU_Get.java
new file mode 100644 (file)
index 0000000..017d86e
--- /dev/null
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.config.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.config.Get;
+
+public class JU_Get {
+
+    private String defaultVal = "some default value";
+
+    private ByteArrayOutputStream outStream;
+
+    private TestBean tb;
+
+    @Before
+    public void setup() {
+        outStream = new ByteArrayOutputStream();
+        System.setOut(new PrintStream(outStream));
+    }
+
+    @After
+    public void tearDown() {
+        System.setOut(System.out);
+    }
+
+    @Test
+    public void beanTest() {
+        tb = new TestBean();
+        tb.setProperty1("prop1");
+
+        Get.Bean testBean = new Get.Bean(tb);
+        assertThat(testBean.get("property1", defaultVal, true), is("prop1"));
+        assertThat(testBean.get("property2", defaultVal, true), is(defaultVal));
+        assertThat(testBean.get("thrower", defaultVal, true), is(defaultVal));
+    }
+
+    @Test
+    public void nullTest() {
+        assertThat(Get.NULL.get("name", defaultVal, true), is(defaultVal));
+    }
+
+    @Test
+    public void accessTest() {
+
+        PropAccess access = new PropAccess();
+        access.setProperty("tag", "value");
+        Get.AccessGet accessGet = new Get.AccessGet(access);
+
+        assertThat(accessGet.get("tag", defaultVal, true), is("value"));
+        outStream.reset();
+
+        assertThat(accessGet.get("not a real tag", defaultVal, true), is(defaultVal));
+        outStream.reset();
+
+        assertThat(accessGet.get("not a real tag", null, true), is(nullValue()));
+
+        outStream.reset();
+
+        assertThat(accessGet.get("tag", defaultVal, false), is("value"));
+        assertThat(outStream.toString(), is(""));
+    }
+
+    public class TestBean implements java.io.Serializable {
+
+        private static final long serialVersionUID = 1L;
+        private String property1 = null;
+        private String property2 = null;
+        @SuppressWarnings("unused")
+        private String thrower = null;
+
+        public TestBean() { }
+        public String getProperty1() { return property1; }
+        public void setProperty1(final String value) { this.property1 = value; }
+        public String getProperty2() { return property2; }
+        public void setProperty2(final String value) { this.property2 = value; }
+        public String getThrower() throws Exception { throw new Exception(); }
+        public void setThrower(final String value) { this.thrower = value; }
+
+    }
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/config/test/JU_GetAccess.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/config/test/JU_GetAccess.java
new file mode 100644 (file)
index 0000000..309810a
--- /dev/null
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.config.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.config.Get;
+import org.onap.ccsdk.apps.cadi.config.GetAccess;
+
+public class JU_GetAccess {
+
+    private String defaultVal = "some default value";
+
+    private ByteArrayOutputStream outStream;
+
+    private PropAccess access;
+    private Get.AccessGet accessGet;
+    private File file;
+    private String filePath;
+
+    @Before
+    public void setup() throws IOException {
+        outStream = new ByteArrayOutputStream();
+        System.setOut(new PrintStream(outStream));
+
+        file = File.createTempFile("GetAccess_test", "");
+        filePath = file.getAbsolutePath();
+
+        access = new PropAccess();
+        access.setProperty("cadi_prop_files", filePath);
+        accessGet = new Get.AccessGet(access);
+
+    }
+
+    @After
+    public void tearDown() {
+        System.setOut(System.out);
+
+        file.delete();
+    }
+
+    @Test
+    public void constructorTest() {
+        String output;
+
+        @SuppressWarnings("unused")
+        GetAccess getAccess = new GetAccess(accessGet);
+        String[] lines = outStream.toString().split(System.lineSeparator());
+        assertThat(lines.length, is(5));
+        output = lines[0].split(" ", 2)[1];
+
+    }
+
+    @Test
+    public void getPropertyTest1() {
+        GetAccess getAccess = new GetAccess(accessGet);
+
+        getAccess.setProperty("tag", "value");
+        assertThat(getAccess.getProperty("tag", defaultVal), is("value"));
+        assertThat(getAccess.getProperty("not_a_tag", defaultVal), is(defaultVal));
+    }
+
+    @Test
+    public void getPropertyTest2() {
+        GetAccess getAccess = new GetAccess(accessGet);
+
+        getAccess.setProperty("tag", "value");
+        assertThat(getAccess.getProperty("tag"), is("value"));
+        assertThat(getAccess.getProperty("not_a_tag"), is(nullValue()));
+    }
+
+    @Test
+    public void getTest() {
+        GetAccess getAccess = new GetAccess(accessGet);
+        assertThat((Get.AccessGet)getAccess.get(), is(accessGet));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/config/test/JU_MapBathConverter.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/config/test/JU_MapBathConverter.java
new file mode 100644 (file)
index 0000000..4be7762
--- /dev/null
@@ -0,0 +1,248 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ */
+
+package org.onap.ccsdk.apps.cadi.config.test;
+
+import java.io.File;
+import java.io.IOException;
+import java.sql.Date;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.GregorianCalendar;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.Symm;
+import org.onap.ccsdk.apps.cadi.filter.MapBathConverter;
+import org.onap.ccsdk.apps.cadi.util.CSV;
+import org.onap.ccsdk.apps.cadi.util.CSV.Visitor;
+import org.onap.ccsdk.apps.cadi.util.CSV.Writer;
+
+import junit.framework.Assert;
+
+/**
+ * Test a simple Migration conversion tool for CADI
+ *
+ * @author Instrumental(Jonathan)
+ *
+ */
+public class JU_MapBathConverter {
+    private static final String NEW_USER_SOMETHING_ORG = "NEW_USER@Something.org";
+    private static final String OLD_ID = "OLD_ID";
+    private static final String SHARED_PASS = "SHARED_PASS";
+    private static CSV csv;
+    private static ArrayList<String> expected;
+    private static final Access access = new PropAccess();
+    private final static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+
+    @BeforeClass
+    public static void createFile() throws IOException {
+        // Note, you cate a "MapBathConverter" by access to a File.
+        // We will create that file now.  Local is fine.
+        csv = new CSV(access,"JU_MapBathConverter.csv");
+    }
+
+    @BeforeClass
+    public static void beforeClass() {
+        expected = new ArrayList<>();
+    }
+
+    @Before
+    public void before() {
+        expected.clear();
+    }
+
+    @Test
+    public void test() throws IOException, CadiException {
+        CSV.Writer cw = csv.writer();
+        GregorianCalendar gc = new GregorianCalendar();
+        gc.add(GregorianCalendar.MONTH, 6);
+        try {
+            try {
+                // CSV can simply be OLD ID and NEW,  no passwords
+                cw.row(exp(OLD_ID), exp(NEW_USER_SOMETHING_ORG),sdf.format(gc.getTime()));
+
+                // Style 1 - Incoming ID/pass, create new cred with NweID and same Pass
+                cw.row(exp(bath(OLD_ID,SHARED_PASS)), exp(NEW_USER_SOMETHING_ORG),sdf.format(gc.getTime()));
+                // the response should be Basic with NEW_ID and OLD_PASS
+
+                // Style 2
+                cw.row(exp(bath(OLD_ID,"OLD_PASS")), exp(bath(NEW_USER_SOMETHING_ORG,"NEW_PASS")),sdf.format(gc.getTime()));
+
+            } finally {
+                cw.close();
+            }
+
+            final Iterator<String> exp = expected.iterator();
+            csv.visit(new Visitor() {
+                @Override
+                public void visit(List<String> row) {
+                    int i=0;
+                    for(String s : row) {
+                        switch(i++) {
+                            case 0:
+                            case 1:
+                                Assert.assertEquals(exp.next(), s);
+                                break;
+                            case 2:
+                                try {
+                                    Date.valueOf(s);
+                                } catch (Exception e) {
+                                    Assert.assertTrue("Last entry should be a date",false);
+                                }
+                                break;
+                            default:
+                                Assert.fail("There should only be 3 columns in this test case.");
+                        }
+                    }
+                }
+            });
+
+            MapBathConverter mbc = new MapBathConverter(access, csv);
+
+            // Check no lookup just returns the same
+            Assert.assertEquals("NonKey", "NonKey"); // if not in map, expect same value
+
+            Iterator<String> exp1 = expected.iterator();
+            // there's no passwords in CSV
+            String old = exp1.next();
+            String nw = exp1.next();
+            Assert.assertEquals(nw, mbc.convert(access,old));
+
+            Assert.assertEquals(bath(NEW_USER_SOMETHING_ORG,SHARED_PASS), mbc.convert(access,bath(OLD_ID,SHARED_PASS)));
+
+            // Style 1 (new cred, old password)
+            old = exp1.next();
+            nw = bath(exp1.next(),SHARED_PASS);
+            Assert.assertEquals(nw, mbc.convert(access,old));
+
+            // Style 2
+            old = exp1.next();
+            nw = exp1.next();
+            Assert.assertEquals(nw, mbc.convert(access,old));
+
+        } finally {
+            csv.delete();
+        }
+    }
+
+    @Test
+    public void testInsecureRole() throws IOException {
+        CSV.Writer cw = csv.writer();
+        GregorianCalendar gc = new GregorianCalendar();
+        gc.add(GregorianCalendar.MONTH, 6);
+        try {
+            try {
+                // Invalid Scenario - Non Authenticated ID to authenticated User
+                cw.row(exp(OLD_ID), exp(bath(NEW_USER_SOMETHING_ORG,"NEW_PASS")),sdf.format(gc.getTime()));
+
+            } finally {
+                cw.close();
+            }
+
+            try {
+                new MapBathConverter(access, csv);
+                Assert.fail("Invalid Data should throw Exception");
+            } catch (CadiException e) {
+                Assert.assertTrue("Invalid Data should throw Exception",true);
+            }
+
+        } finally {
+            csv.delete();
+        }
+    }
+
+    @Test
+    public void testTooFewColumns() throws IOException, CadiException {
+        CSV.Writer cw = csv.writer();
+        try {
+            try {
+                cw.row(exp(bath(OLD_ID,"OLD_PASS")), exp(bath(NEW_USER_SOMETHING_ORG,"NEW_PASS")));
+            } finally {
+                cw.close();
+            }
+
+            try {
+                new MapBathConverter(access, csv);
+                Assert.fail("file with too few rows should throw exception");
+            } catch(CadiException | IOException e) {
+                Assert.assertTrue("Correctly thrown Exception",true);
+            }
+        } finally {
+            csv.delete();
+        }
+    }
+
+    @Test
+    public void testNoFile() {
+        try {
+            new MapBathConverter(access, new CSV(access,"Bogus"));
+            Assert.fail("Non Existent File should throw exception");
+        } catch(CadiException | IOException e) {
+            Assert.assertTrue("Correctly thrown Exception",true);
+        }
+    }
+
+    @Test
+    public void testBadRows() throws IOException {
+        try {
+            Writer cw = csv.writer();
+            try {
+                cw.row("Single Column");
+            } finally {
+                cw.close();
+            }
+
+            try {
+                new MapBathConverter(access,csv);
+                Assert.fail("Non Existent File should throw exception");
+            } catch(CadiException | IOException e) {
+                Assert.assertTrue("Correctly thrown Exception",true);
+            }
+        } finally {
+            csv.delete();
+        }
+
+        // Check for deletion
+        Assert.assertFalse(csv.toString() + "should have been deleted",new File(csv.toString()).exists());
+    }
+
+    private String bath(String user, String password) throws IOException {
+        StringBuilder sb = new StringBuilder(user);
+        sb.append(':');
+        sb.append(password);
+        byte[] encoded = Symm.base64noSplit.encode(sb.toString().getBytes());
+        sb.setLength(0);
+        sb.append("Basic ");
+        sb.append(new String(encoded));
+        return sb.toString();
+    }
+
+    private String exp(String s) {
+        expected.add(s);
+        return s;
+    }
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/config/test/JU_MultiGet.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/config/test/JU_MultiGet.java
new file mode 100644 (file)
index 0000000..ccaf359
--- /dev/null
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.config.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.config.Get;
+import org.onap.ccsdk.apps.cadi.config.MultiGet;
+
+public class JU_MultiGet {
+
+    private String defaultVal = "some default value";
+
+    private ByteArrayOutputStream outStream;
+
+    private MultiGet multiGet;
+    private Get.AccessGet accessGet;
+    private PropAccess access;
+
+    @Before
+    public void setup() throws IOException {
+        outStream = new ByteArrayOutputStream();
+        System.setOut(new PrintStream(outStream));
+
+        access = new PropAccess();
+        access.setProperty("tag", "value");
+        accessGet = new Get.AccessGet(access);
+        multiGet = new MultiGet(accessGet, Get.NULL);
+    }
+
+    @After
+    public void tearDown() {
+        System.setOut(System.out);
+    }
+
+    @Test
+    public void getTest() {
+        assertThat(multiGet.get("tag", defaultVal, false), is("value"));
+        assertThat(multiGet.get("not_a_tag", defaultVal, false), is(defaultVal));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/config/test/JU_RegistrationPropHolder.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/config/test/JU_RegistrationPropHolder.java
new file mode 100644 (file)
index 0000000..6a51815
--- /dev/null
@@ -0,0 +1,149 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ */
+
+package org.onap.ccsdk.apps.cadi.config.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.net.UnknownHostException;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.config.Config;
+import org.onap.ccsdk.apps.cadi.config.RegistrationPropHolder;
+
+public class JU_RegistrationPropHolder {
+
+    @Test
+    public void testBlank() {
+        PropAccess pa = new PropAccess();
+        RegistrationPropHolder rph;
+        int ju_port = 20;
+        try {
+            ////////////////
+            // Check Required Properties
+            ////////////////
+            try {
+                rph = new RegistrationPropHolder(pa,20);
+            } catch (CadiException e) {
+                Assert.assertEquals(
+                        "\ncadi_latitude must be defined." +
+                        "\ncadi_longitude must be defined.",e.getMessage());
+            }
+
+            try {
+                pa.setProperty(Config.CADI_LATITUDE, "32.7");
+                rph = new RegistrationPropHolder(pa,20);
+            } catch (CadiException e) {
+                Assert.assertEquals(
+                        "\ncadi_longitude must be defined.",e.getMessage());
+            }
+
+            pa.setProperty(Config.CADI_LONGITUDE, "-72.0");
+            rph = new RegistrationPropHolder(pa,ju_port);
+
+            ////////////////
+            // Validate Default Properties
+            ////////////////
+            for(String dot_le : new String[] {"",".helm"}) {
+                assertEquals(rph.hostname,rph.default_fqdn);
+                assertEquals("",rph.lcontainer);
+                assertEquals(rph.hostname,rph.public_fqdn);
+                assertEquals(ju_port,rph.getEntryPort(dot_le));
+                assertEquals(rph.hostname,rph.getEntryFQDN("",dot_le));
+            }
+
+            String ns = "myns";
+            pa.setProperty(Config.AAF_LOCATOR_APP_NS, ns);
+            for(String dot_le : new String[] {"",".helm"}) {
+                assertEquals(rph.hostname,rph.default_fqdn);
+                assertEquals("",rph.lcontainer);
+                assertEquals(rph.hostname,rph.public_fqdn);
+                assertEquals(ju_port,rph.getEntryPort(dot_le));
+                assertEquals(rph.hostname,rph.getEntryFQDN("",dot_le));
+            }
+
+            String ns2 = "onap";
+            pa.setProperty(Config.AAF_LOCATOR_APP_NS+".helm", ns2);
+            for(String dot_le : new String[] {"",".helm"}) {
+                assertEquals(rph.hostname,rph.default_fqdn);
+                assertEquals("",rph.lcontainer);
+                assertEquals(rph.hostname,rph.public_fqdn);
+                assertEquals(ju_port,rph.getEntryPort(dot_le));
+                assertEquals(rph.hostname,rph.getEntryFQDN("",dot_le));
+            }
+
+            ////////////////
+            // Validate Public Host and Port settings
+            ////////////////
+            String public_hostname = "com.public.hostname";
+            int public_port = 999;
+            pa.setProperty(Config.AAF_LOCATOR_PUBLIC_FQDN, public_hostname);
+            pa.setProperty(Config.AAF_LOCATOR_PUBLIC_PORT,Integer.toString(public_port));
+            RegistrationPropHolder pubRPH = new RegistrationPropHolder(pa,ju_port);
+            assertEquals(public_hostname,pubRPH.public_fqdn);
+            assertEquals(public_port,pubRPH.getEntryPort(""));
+
+
+            final String url = "https://aaf.osaaf.org:8095/org.osaaf.aaf.service:2.1";
+            String name="theName";
+            assertEquals(url,rph.replacements(getClass().getSimpleName(),url, name, ""));
+
+            String alu = "aaf.osaaf.org:8095";
+            String curl = url.replace(alu, Config.AAF_LOCATE_URL_TAG);
+            pa.setProperty(Config.AAF_LOCATE_URL,"https://"+alu);
+            assertEquals(url.replace("8095","8095/locate"),rph.replacements(getClass().getSimpleName(),curl, name, ""));
+
+            String root_ns = "org.osaaf.aaf";
+            curl = url.replace(root_ns, "AAF_NS");
+            pa.setProperty(Config.AAF_ROOT_NS,root_ns);
+            assertEquals(url,rph.replacements(getClass().getSimpleName(),curl, name, ""));
+
+            curl = url.replace(root_ns, "%AAF_NS");
+            pa.setProperty(Config.AAF_ROOT_NS,root_ns);
+            assertEquals(url,rph.replacements(getClass().getSimpleName(),curl, name, ""));
+
+            final String fqdn = "%C.%CNS.%NS.%N";
+            String target = "myns.theName";
+            assertEquals(target,rph.replacements(getClass().getSimpleName(),fqdn, name, ""));
+
+            pa.setProperty(Config.AAF_LOCATOR_CONTAINER_NS+".hello", "mycontns");
+            target = "mycontns.myns.theName";
+            assertEquals(target,rph.replacements(getClass().getSimpleName(),fqdn, name, ".hello"));
+
+            pa.setProperty(Config.AAF_LOCATOR_CONTAINER+".hello","helloC");
+            target = "helloC.mycontns.myns.theName";
+            assertEquals(target,rph.replacements(getClass().getSimpleName(),fqdn, name, ".hello"));
+
+            pa.setProperty(Config.AAF_LOCATOR_CONTAINER_NS,"c_ns");
+            target = "c_ns.myns.theName";
+            assertEquals(target,rph.replacements(getClass().getSimpleName(),fqdn, name, ""));
+
+
+        } catch (UnknownHostException | CadiException e) {
+            e.printStackTrace();
+            Assert.fail();
+        }
+    }
+
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/config/test/JU_SecurityInfo.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/config/test/JU_SecurityInfo.java
new file mode 100644 (file)
index 0000000..99a57e3
--- /dev/null
@@ -0,0 +1,136 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.config.test;
+
+
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.config.Config;
+import org.onap.ccsdk.apps.cadi.config.SecurityInfo;
+
+public class JU_SecurityInfo {
+
+    private static PropAccess access;
+
+    private static final String keyStoreFileName = "src/test/resources/keystore.p12";
+    private static final String keyStorePassword = "Password for the keystore";
+    private static final String keyPassword = "Password for the key";
+
+    private static final String trustStoreFileName = "src/test/resources/truststore.jks";
+    private static final String trustStorePasswd = "Password for the truststore";
+
+    @BeforeClass
+    public static void setupOnce() throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException {
+        KeyStore keyStore = KeyStore.getInstance("PKCS12");
+        keyStore.load(null, null);
+        keyStore.store(new FileOutputStream(keyStoreFileName), keyStorePassword.toCharArray());
+
+        KeyStore trustStore = KeyStore.getInstance("JKS");
+        trustStore.load(null, null);
+        trustStore.store(new FileOutputStream(trustStoreFileName), trustStorePasswd.toCharArray());
+    }
+
+    @Before
+    public void setup() throws IOException {
+        access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]);
+
+        access.setProperty(Config.CADI_KEYSTORE, keyStoreFileName);
+        access.setProperty(Config.CADI_KEYSTORE_PASSWORD, access.encrypt(keyStorePassword));
+        access.setProperty(Config.CADI_KEY_PASSWORD, access.encrypt(keyPassword));
+
+        access.setProperty(Config.CADI_TRUSTSTORE, trustStoreFileName);
+        access.setProperty(Config.CADI_TRUSTSTORE_PASSWORD, access.encrypt(trustStorePasswd));
+    }
+
+    @AfterClass
+    public static void tearDownOnce() {
+        File keyStoreFile = new File(keyStoreFileName);
+        if (keyStoreFile.exists()) {
+            keyStoreFile.delete();
+        }
+        File trustStoreFile = new File(trustStoreFileName);
+        if (trustStoreFile.exists()) {
+            trustStoreFile.delete();
+        }
+    }
+
+    @Test
+    public void test() throws CadiException {
+        SecurityInfo si = new SecurityInfo(access);
+
+        assertNotNull(si.getSSLSocketFactory());
+        assertNotNull(si.getSSLContext());
+        assertNotNull(si.getKeyManagers());
+
+        access.setProperty(Config.CADI_TRUST_MASKS, "123.123.123.123");
+        si = new SecurityInfo(access);
+    }
+
+    @Test(expected = CadiException.class)
+    public void nullkeyStoreTest() throws CadiException {
+        access.setProperty(Config.CADI_KEYSTORE, "passwords.txt");
+        @SuppressWarnings("unused")
+        SecurityInfo si = new SecurityInfo(access);
+    }
+
+    @Test(expected = CadiException.class)
+    public void nullTrustStoreTest() throws CadiException {
+        access.setProperty(Config.CADI_TRUSTSTORE, "passwords.txt");
+        @SuppressWarnings("unused")
+        SecurityInfo si = new SecurityInfo(access);
+    }
+
+
+    @Test(expected = NumberFormatException.class)
+    public void badTrustMaskTest() throws CadiException {
+        access.setProperty(Config.CADI_TRUST_MASKS, "trustMask");
+        @SuppressWarnings("unused")
+        SecurityInfo si = new SecurityInfo(access);
+    }
+
+    @Test
+    public void coverageTest() throws CadiException {
+        PropAccess badAccess = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]);
+        @SuppressWarnings("unused")
+        SecurityInfo si = new SecurityInfo(badAccess);
+        badAccess.setProperty(Config.CADI_KEYSTORE, keyStoreFileName);
+        si = new SecurityInfo(badAccess);
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/config/test/JU_SecurityInfoC.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/config/test/JU_SecurityInfoC.java
new file mode 100644 (file)
index 0000000..e726c9f
--- /dev/null
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.config.test;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.junit.*;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.SecuritySetter;
+import org.onap.ccsdk.apps.cadi.config.SecurityInfoC;
+
+public class JU_SecurityInfoC {
+
+    ByteArrayOutputStream outStream;
+    ByteArrayOutputStream errStream;
+
+    @Before
+    public void setup() {
+        outStream = new ByteArrayOutputStream();
+        errStream = new ByteArrayOutputStream();
+
+        System.setOut(new PrintStream(outStream));
+        System.setErr(new PrintStream(errStream));
+    }
+
+    @After
+    public void tearDown() {
+        System.setOut(System.out);
+        System.setErr(System.err);
+    }
+
+//    @Test
+//    public void instanceTest() throws CadiException, MalformedURLException {
+//        SecurityInfoC<HttpURLConnection> si = SecurityInfoC.instance(new PropAccess(), HttpURLConnection.class );
+//        assertThat(si.defSS.getID(), is(SecurityInfoC.DEF_ID));
+//        try {
+//            si.defSS.setSecurity(new HttpURLConnectionStub());
+//            fail("Should have thrown an exception");
+//        } catch (CadiException e) {
+//            assertTrue(e instanceof CadiException);
+//            assertThat(e.getMessage(), is("No Client Credentials set."));
+//        }
+//        assertThat(si.defSS.setLastResponse(0), is(0));
+//
+//        // Try it again for coverage
+//        SecurityInfoC<HttpURLConnection> siClone = SecurityInfoC.instance(new PropAccess(), HttpURLConnection.class);
+//        assertThat(siClone, is(si));
+//    }
+
+    @Test
+    public void setTest() throws MalformedURLException, CadiException {
+        SecurityInfoC<HttpURLConnectionStub> si = SecurityInfoC.instance(new PropAccess(), HttpURLConnectionStub.class);
+        SecuritySetter<HttpURLConnectionStub> ss = new SecuritySetterStub<HttpURLConnectionStub>();
+        assertThat(si.set(ss), is(si));
+        assertThat(si.defSS.getID(), is("Example ID"));
+        try {
+            si.defSS.setSecurity(new HttpURLConnectionStub());
+            fail("Should have thrown an exception");
+        } catch (CadiException e) {
+            assertTrue(e instanceof CadiException);
+            assertThat(e.getMessage(), is("Example exception"));
+        }
+        assertThat(si.defSS.setLastResponse(0), is(0));
+        assertThat(si.defSS.setLastResponse(1), is(1));
+        assertThat(si.defSS.setLastResponse(-1), is(-1));
+    }
+
+    public static class HttpURLConnectionStub extends HttpURLConnection {
+        public HttpURLConnectionStub() throws MalformedURLException { super(new URL("http://www.example.com")); }
+        @Override public void disconnect() { }
+        @Override public boolean usingProxy() { return false; }
+        @Override public void connect() throws IOException { }
+    }
+
+    private class SecuritySetterStub<CT> implements SecuritySetter<CT> {
+        public String getID() { return "Example ID"; }
+        public void setSecurity(CT client) throws CadiException { throw new CadiException("Example exception"); }
+        public int setLastResponse(int respCode) { return respCode; }
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/config/test/JU_UsersDump.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/config/test/JU_UsersDump.java
new file mode 100644 (file)
index 0000000..dfac216
--- /dev/null
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.config.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import org.onap.ccsdk.apps.cadi.AbsUserCache;
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.config.UsersDump;
+import org.onap.ccsdk.apps.cadi.lur.LocalLur;
+import org.onap.ccsdk.apps.cadi.lur.LocalPermission;
+import org.onap.ccsdk.apps.cadi.util.Split;
+
+public class JU_UsersDump {
+
+    private ByteArrayOutputStream outStream;
+    private ByteArrayOutputStream stdoutSuppressor;
+
+    private static final String expected = "<?xml version='1.0' encoding='utf-8'?>\n" +
+        "<!--\n" +
+        "    Code Generated Tomcat Users and Roles from AT&T LUR on ...\n" +
+        "-->\n" +
+        "<tomcat-users>\n" +
+        "  <role rolename=\"suser\"/>\n" +
+        "  <role rolename=\"admin\"/>\n" +
+        "  <role rolename=\"groupB\"/>\n" +
+        "  <role rolename=\"groupA\"/>\n" +
+        "  \n" +
+        "  <user username=\"hisname@people.osaaf.org\" roles=\"suser\"/>\n" +
+        "  <user username=\"yourname@people.osaaf.org\" roles=\"admin\"/>\n" +
+        "  <user username=\"myname@people.osaaf.org\" roles=\"admin\"/>\n" +
+        "  <user username=\"m1234@people.osaaf.org\" roles=\"suser\"/>\n" +
+        "  <user username=\"myname\" roles=\"groupB,groupA\"/>\n" +
+        "  <user username=\"hername@people.osaaf.org\" roles=\"suser\"/>\n" +        
+        "</tomcat-users>\n";
+
+    private final static String groups = "myname:groupA,groupB";
+    private final static String names = "admin:myname,yourname;suser:hisname,hername,m1234";
+
+    private AbsUserCache<LocalPermission> lur;
+
+    @Before
+    public void setup() throws IOException {
+        outStream = new ByteArrayOutputStream();
+        stdoutSuppressor = new ByteArrayOutputStream();
+
+        System.setOut(new PrintStream(stdoutSuppressor));
+
+        lur = new LocalLur(new PropAccess(), groups, names);
+    }
+
+    @After
+    public void tearDown() {
+        System.setOut(System.out);
+    }
+
+    @Test
+    public void writeTest() throws IOException {
+        UsersDump.write(outStream, lur);
+        String[] actualLines = Split.splitTrim('\n', outStream.toString());
+        String[] expectedLines = Split.splitTrim('\n', expected);
+        for (String s : actualLines) {
+            System.out.println(s);
+        }
+
+        assertThat(actualLines.length, is(expectedLines.length));
+
+        // Check that the output starts with an XML tag
+        assertThat(actualLines[0], is(expectedLines[0]));
+        // Check that lines 2-4 are a comment
+        assertThat(actualLines[1], is(expectedLines[1]));
+        assertThat(actualLines[3], is(expectedLines[3]));
+
+        // Check that the rest of the output matches the expected output
+        for (int i = 4; i < actualLines.length; i++) {
+            assertThat(actualLines[i], is(expectedLines[i]));
+        }
+
+        // Run the test again with outStream as a PrintStream (for coverage)
+        outStream.reset();
+        UsersDump.write(new PrintStream(outStream), lur);
+        actualLines = Split.splitTrim('\n', outStream.toString());
+
+        assertThat(actualLines.length, is(expectedLines.length));
+
+        // Check that the output starts with an XML tag
+        assertThat(actualLines[0], is(expectedLines[0]));
+        // Check that lines 2-4 are a comment
+        assertThat(actualLines[1], is(expectedLines[1]));
+        assertThat(actualLines[3], is(expectedLines[3]));
+
+        // Check that the rest of the output matches the expected output
+        for (int i = 4; i < actualLines.length; i++) {
+            assertThat(actualLines[i], is(expectedLines[i]));
+        }
+    }
+
+    @Test
+    public void updateUsersTest() {
+        String output;
+        File outputFile = new File("src/test/resources/userdump.xml");
+        assertThat(outputFile.exists(), is(false));
+
+        output = UsersDump.updateUsers("src/test/resources/userdump.xml", (LocalLur) lur);
+        assertThat(output, is(nullValue()));
+        assertThat(outputFile.exists(), is(true));
+
+        output = UsersDump.updateUsers("src/test/resources/userdump.xml", (LocalLur) lur);
+        assertThat(output, is(nullValue()));
+        assertThat(outputFile.exists(), is(true));
+
+        outputFile.delete();
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/filter/test/JU_AUTHZServlet.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/filter/test/JU_AUTHZServlet.java
new file mode 100644 (file)
index 0000000..a89f4a3
--- /dev/null
@@ -0,0 +1,107 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.filter.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.ccsdk.apps.cadi.filter.AUTHZServlet;
+
+import jakarta.servlet.Servlet;
+import jakarta.servlet.ServletConfig;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequestWrapper;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+public class JU_AUTHZServlet {
+
+    @Mock private Servlet servletMock;
+    @Mock private ServletConfig servletConfigMock;
+    @Mock private HttpServletRequest reqMock;
+    @Mock private HttpServletResponse respMock;
+    @Mock private ServletRequestWrapper servletWrapperMock;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void test() throws ServletException, IOException {
+        AUTHZServletStub servlet = new AUTHZServletStub(Servlet.class);
+
+        try {
+            servlet.init(servletConfigMock);
+            fail("Should've thrown an exception");
+        } catch (ServletException e) {
+            assertThat(e.getMessage(), is("Invalid Servlet Delegate"));
+        }
+
+        setPrivateField(AUTHZServlet.class, "delegate", servlet, servletMock);
+        servlet.init(servletConfigMock);
+        servlet.getServletConfig();
+        servlet.getServletInfo();
+
+        servlet.service(reqMock, respMock);
+
+        String[] roles = new String[] {"role1", "role2"};
+        setPrivateField(AUTHZServlet.class, "roles", servlet, roles);
+        servlet.service(reqMock, respMock);
+
+        when(reqMock.isUserInRole("role1")).thenReturn(true);
+        servlet.service(reqMock, respMock);
+
+        try {
+            servlet.service(servletWrapperMock, respMock);
+            fail("Should've thrown an exception");
+        } catch (ServletException e) {
+            assertThat(e.getMessage(), is("JASPIServlet only supports HTTPServletRequest/HttpServletResponse"));
+        }
+        servlet.destroy();
+    }
+
+    private class AUTHZServletStub extends AUTHZServlet<Servlet> {
+        public AUTHZServletStub(Class<Servlet> cls) { super(cls); }
+    }
+
+    private void setPrivateField(Class<?> clazz, String fieldName, Object target, Object value) {
+        try {
+            Field field = clazz.getDeclaredField(fieldName);
+            field.setAccessible(true);
+            field.set(target, value);
+            field.setAccessible(false);
+        } catch (Exception e) {
+            System.err.println("Could not set field [" + fieldName + "] to " + value);
+        }
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/filter/test/JU_AccessGetter.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/filter/test/JU_AccessGetter.java
new file mode 100644 (file)
index 0000000..ce384b0
--- /dev/null
@@ -0,0 +1,54 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.filter.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.filter.AccessGetter;
+
+public class JU_AccessGetter {
+
+    private static final String tag = "tag";
+    private static final String value = "value";
+
+    private PropAccess access;
+
+    @Before
+    public void setup() {
+        access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]);
+        access.setProperty(tag, value);
+    }
+
+    @Test
+    public void test() {
+        AccessGetter getter = new AccessGetter(access);
+        assertThat(getter.get(tag, null, false), is(value));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/filter/test/JU_MapPermConverter.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/filter/test/JU_MapPermConverter.java
new file mode 100644 (file)
index 0000000..65d0c2d
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.filter.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import org.junit.Test;
+import org.onap.ccsdk.apps.cadi.filter.MapPermConverter;
+
+public class JU_MapPermConverter {
+
+    private static final String tag = "tag";
+    private static final String value = "value";
+    private static final String nontag = "nontag";
+
+    @Test
+    public void test() {
+        MapPermConverter converter = new MapPermConverter();
+        assertThat(converter.map().isEmpty(), is(true));
+        converter.map().put(tag, value);
+        assertThat(converter.convert(tag), is(value));
+        assertThat(converter.convert(nontag), is(nontag));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/filter/test/JU_NullPermConverter.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/filter/test/JU_NullPermConverter.java
new file mode 100644 (file)
index 0000000..92b467f
--- /dev/null
@@ -0,0 +1,38 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.filter.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import org.junit.Test;
+import org.onap.ccsdk.apps.cadi.filter.NullPermConverter;
+
+public class JU_NullPermConverter {
+
+    @Test
+    public void test() {
+        NullPermConverter converter = NullPermConverter.singleton();
+        assertThat(converter.convert("test"), is("test"));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/filter/test/JU_PathFilter.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/filter/test/JU_PathFilter.java
new file mode 100644 (file)
index 0000000..6a133b1
--- /dev/null
@@ -0,0 +1,105 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.filter.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.security.Principal;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.config.Config;
+import org.onap.ccsdk.apps.cadi.filter.PathFilter;
+
+public class JU_PathFilter {
+
+    private PropAccess access;
+
+    @Mock private FilterConfig filterConfigMock;
+    @Mock private ServletContext contextMock;
+    @Mock private HttpServletRequest reqMock;
+    @Mock private HttpServletResponse respMock;
+    @Mock private FilterChain chainMock;
+    @Mock private Principal princMock;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(filterConfigMock.getServletContext()).thenReturn(contextMock);
+        when(reqMock.getUserPrincipal()).thenReturn(princMock);
+        when(princMock.getName()).thenReturn("name");
+
+        access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]);
+    }
+
+    @Test
+    public void test() throws ServletException, IOException {
+        PathFilter pathFilter = new PathFilter(access);
+        try {
+            pathFilter.init(filterConfigMock);
+            fail("Should've thrown an exception");
+        } catch (ServletException e) {
+            assertThat(e.getMessage(), is("PathFilter - pathfilter_ns is not set"));
+        }
+
+        when(contextMock.getAttribute(Config.PATHFILTER_NS)).thenReturn(5);
+        when(contextMock.getAttribute(Config.PATHFILTER_STACK)).thenReturn(5);
+        when(contextMock.getAttribute(Config.PATHFILTER_URLPATTERN)).thenReturn(5);
+        when(contextMock.getAttribute(Config.PATHFILTER_NOT_AUTHORIZED_MSG)).thenReturn(5);
+        pathFilter.init(filterConfigMock);
+
+        pathFilter.doFilter(reqMock, respMock, chainMock);
+
+        when(reqMock.isUserInRole(anyString())).thenReturn(true);
+        pathFilter.doFilter(reqMock, respMock, chainMock);
+
+        pathFilter.destroy();
+
+        pathFilter = new PathFilter();
+        pathFilter.init(filterConfigMock);
+
+        pathFilter.doFilter(reqMock, respMock, chainMock);
+
+        when(reqMock.isUserInRole(anyString())).thenReturn(false);
+        pathFilter.doFilter(reqMock, respMock, chainMock);
+
+        pathFilter.destroy();
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/lur/test/JU_ConfigPrincipal.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/lur/test/JU_ConfigPrincipal.java
new file mode 100644 (file)
index 0000000..47ccb81
--- /dev/null
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.lur.test;
+
+import org.junit.*;
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import java.lang.reflect.Field;
+
+import java.io.IOException;
+
+import org.onap.ccsdk.apps.cadi.lur.ConfigPrincipal;
+
+public class JU_ConfigPrincipal {
+
+    private final String name = "User";
+    private final String pass = "pass";
+
+    // Expected output of base64("User:pass")
+    private final String b64encoded = "VXNlcjpwYXNz";
+
+    private Field content_field;
+
+    @Before
+    public void setup() throws NoSuchFieldException {
+        content_field = ConfigPrincipal.class.getDeclaredField("content");
+        content_field.setAccessible(true);
+    }
+
+    @Test
+    public void testConfigPrincipalStringString() throws IOException, IllegalArgumentException, IllegalAccessException {
+        ConfigPrincipal p =  new ConfigPrincipal(name, pass);
+
+        assertThat(p.getName(), is(name));
+        assertThat(p.toString(), is(name));
+        assertThat(p.getCred(), is(pass.getBytes()));
+        assertThat(p.getAsBasicAuthHeader(), is("Basic " + b64encoded));
+        content_field.set(p, "pass");
+        assertThat(p.getAsBasicAuthHeader(), is("Basic " + b64encoded));
+
+        // One more time for coverage purposes
+        assertThat(p.getAsBasicAuthHeader(), is("Basic " + b64encoded));
+    }
+
+    @Test
+    public void testConfigPrincipalStringByteArray() throws IOException, IllegalArgumentException, IllegalAccessException {
+        ConfigPrincipal p =  new ConfigPrincipal(name, pass.getBytes());
+
+        assertThat(p.getName(), is(name));
+        assertThat(p.toString(), is(name));
+        assertThat(p.getCred(), is(pass.getBytes()));
+        assertThat(p.getAsBasicAuthHeader(), is("Basic " + b64encoded));
+        content_field.set(p, "pass");
+        assertThat(p.getAsBasicAuthHeader(), is("Basic " + b64encoded));
+
+        // One more time for coverage purposes
+        assertThat(p.getAsBasicAuthHeader(), is("Basic " + b64encoded));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/lur/test/JU_EpiLur.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/lur/test/JU_EpiLur.java
new file mode 100644 (file)
index 0000000..3718308
--- /dev/null
@@ -0,0 +1,128 @@
+/**
+ *
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.lur.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.when;
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.ccsdk.apps.cadi.CachingLur;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.CredVal;
+import org.onap.ccsdk.apps.cadi.Lur;
+import org.onap.ccsdk.apps.cadi.Permission;
+import org.onap.ccsdk.apps.cadi.lur.EpiLur;
+
+public class JU_EpiLur {
+
+    private ArrayList<Permission> perms;
+    private CredValStub lurMock3;
+
+    @Mock private Lur lurMock1;
+    @Mock private CachingLur<?> lurMock2;
+    @Mock private Principal princMock;
+    @Mock private Permission permMock;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        perms = new ArrayList<>();
+        perms.add(permMock);
+
+        lurMock3 = new CredValStub();
+    }
+
+    @Test
+    public void test() throws CadiException {
+        EpiLur lur;
+        try {
+            lur = new EpiLur();
+        } catch (CadiException e) {
+            assertThat(e.getMessage(), is("Need at least one Lur implementation in constructor"));
+        }
+        lur = new EpiLur(lurMock1, lurMock2, lurMock3);
+        assertThat(lur.fish(null,  null), is(false));
+
+        assertThat(lur.fish(princMock, permMock), is(false));
+
+        when(lurMock2.handlesExclusively(permMock)).thenReturn(true);
+        assertThat(lur.fish(princMock, permMock), is(false));
+
+        when(lurMock2.fish(princMock, permMock)).thenReturn(true);
+        assertThat(lur.fish(princMock, permMock), is(true));
+
+        lur.fishAll(princMock, perms);
+
+        assertThat(lur.handlesExclusively(permMock), is(false));
+
+        assertThat(lur.get(-1), is(nullValue()));
+        assertThat(lur.get(0), is(lurMock1));
+        assertThat(lur.get(1), is((Lur)lurMock2));
+        assertThat(lur.get(2), is((Lur)lurMock3));
+        assertThat(lur.get(3), is(nullValue()));
+
+        assertThat(lur.handles(princMock), is(false));
+        when(lurMock2.handles(princMock)).thenReturn(true);
+        assertThat(lur.handles(princMock), is(true));
+
+        lur.remove("id");
+
+        lur.clear(princMock, null);
+
+        assertThat(lur.createPerm("perm"), is(not(nullValue())));
+
+        lur.getUserPassImpl();
+        assertThat(lur.getUserPassImpl(), is((CredVal)lurMock3));
+
+        lur.toString();
+        lur.destroy();
+
+        lur = new EpiLur(lurMock1, lurMock2);
+        assertThat(lur.getUserPassImpl(), is(nullValue()));
+
+        assertThat(lur.subLur(Lur.class), is(nullValue()));
+    }
+
+    private class CredValStub implements Lur, CredVal {
+        @Override public boolean validate(String user, Type type, byte[] cred, Object state) { return false; }
+        @Override public Permission createPerm(String p) { return null; }
+        @Override public boolean fish(Principal bait, Permission ... pond) { return false; }
+        @Override public void fishAll(Principal bait, List<Permission> permissions) { }
+        @Override public void destroy() { }
+        @Override public boolean handlesExclusively(Permission ... pond) { return false; }
+        @Override public boolean handles(Principal principal) { return false; }
+        @Override public void clear(Principal p, StringBuilder report) { }
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/lur/test/JU_LocalLur.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/lur/test/JU_LocalLur.java
new file mode 100644 (file)
index 0000000..5185011
--- /dev/null
@@ -0,0 +1,173 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.lur.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.ccsdk.apps.cadi.AbsUserCache;
+import org.onap.ccsdk.apps.cadi.CredVal.Type;
+import org.onap.ccsdk.apps.cadi.Permission;
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.lur.ConfigPrincipal;
+import org.onap.ccsdk.apps.cadi.lur.LocalLur;
+import org.onap.ccsdk.apps.cadi.lur.LocalPermission;
+
+public class JU_LocalLur {
+
+    private PropAccess access;
+    private ByteArrayOutputStream outStream;
+
+    @Mock Permission permMock;
+
+    @Before
+    public void setup() throws IOException {
+        MockitoAnnotations.initMocks(this);
+
+        outStream = new ByteArrayOutputStream();
+        access = new PropAccess(new PrintStream(outStream), new String[0]) {
+            @Override public String decrypt(String encrypted, boolean anytext) throws IOException {
+                return rot13(encrypted);
+            }
+            @Override public String encrypt(String unencrypted) throws IOException {
+                return rot13(unencrypted);
+            }
+        };
+
+    }
+
+    @Test
+    public void test() throws IOException {
+        final String password = "<pass>";
+        final String encrypted = rot13(password);
+
+        LocalLur lur;
+        List<AbsUserCache<LocalPermission>.DumpInfo> info;
+
+        lur = new LocalLur(access, null, null);
+        assertThat(lur.dumpInfo().size(), is(0));
+
+        lur = new LocalLur(access, "user1", null);
+        info = lur.dumpInfo();
+        assertThat(info.size(), is(1));
+        assertThat(info.get(0).user, is("user1"));
+
+        lur.clearAll();
+        assertThat(lur.dumpInfo().size(), is(0));
+
+        lur = new LocalLur(access, "user1%" + encrypted, null);
+        info = lur.dumpInfo();
+        assertThat(info.size(), is(1));
+        assertThat(info.get(0).user, is("user1@people.osaaf.org"));
+
+        lur.clearAll();
+        assertThat(lur.dumpInfo().size(), is(0));
+
+        lur = new LocalLur(access, "user1@domain%" + encrypted, null);
+        info = lur.dumpInfo();
+        assertThat(info.size(), is(1));
+        assertThat(info.get(0).user, is("user1@domain"));
+
+        lur = new LocalLur(access, "user1@domain%" + encrypted + ":groupA", null);
+        info = lur.dumpInfo();
+        assertThat(info.size(), is(1));
+        assertThat(info.get(0).user, is("user1@domain"));
+
+        when(permMock.getKey()).thenReturn("groupA");
+        assertThat(lur.handlesExclusively(permMock), is(true));
+        when(permMock.getKey()).thenReturn("groupB");
+        assertThat(lur.handlesExclusively(permMock), is(false));
+
+        assertThat(lur.fish(null, null), is(false));
+
+        Principal princ = new ConfigPrincipal("user1@localized", encrypted);
+
+        lur = new LocalLur(access, "user1@localized%" + password + ":groupA", null);
+        assertThat(lur.fish(princ, lur.createPerm("groupA")), is(true));
+        assertThat(lur.fish(princ, lur.createPerm("groupB")), is(false));
+        assertThat(lur.fish(princ, permMock), is(false));
+
+        princ = new ConfigPrincipal("user1@domain", encrypted);
+        assertThat(lur.fish(princ, lur.createPerm("groupB")), is(false));
+
+        princ = new ConfigPrincipal("user1@localized", "badpass");
+        assertThat(lur.fish(princ, lur.createPerm("groupB")), is(false));
+
+        assertThat(lur.handles(null), is(false));
+
+        lur.fishAll(null,  null);
+
+        List<Permission> perms = new ArrayList<>();
+        perms.add(lur.createPerm("groupB"));
+        perms.add(lur.createPerm("groupA"));
+        princ = new ConfigPrincipal("user1@localized", encrypted);
+        lur.fishAll(princ, perms);
+        princ = new ConfigPrincipal("user1@localized", "badpass");
+        lur.fishAll(princ, perms);
+
+        assertThat(lur.validate(null, null, null, null), is(false));
+        assertThat(lur.validate("user", null, "badpass".getBytes(), null), is(false));
+        assertThat(lur.validate("user1@localized", null, encrypted.getBytes(), null), is(false));
+
+        lur = new LocalLur(access, "user1@localized%" + password + ":groupA", null);
+        // Inconsistent on Jenkins only.
+        //assertThat(lur.validate("user1@localized", Type.PASSWORD, encrypted.getBytes(), null), is(true));
+
+        lur = new LocalLur(access, null, "admin");
+        lur = new LocalLur(access, null, "admin:user1");
+        lur = new LocalLur(access, null, "admin:user1@localized");
+        lur = new LocalLur(access, null, "admin:user1@localized,user2@localized%" + password + ";user:user1@localized");
+    }
+
+    public static String rot13(String input) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < input.length(); i++) {
+            char c = input.charAt(i);
+            if (c >= 'a' && c <= 'm') {
+                c += 13;
+            } else if (c >= 'A' && c <= 'M') {
+                c += 13;
+            } else if (c >= 'n' && c <= 'z') {
+                c -= 13;
+            } else if (c >= 'N' && c <= 'Z') {
+                c -= 13;
+            }
+            sb.append(c);
+        }
+        return sb.toString();
+    }
+
+}
+
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/lur/test/JU_LocalPermission.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/lur/test/JU_LocalPermission.java
new file mode 100644 (file)
index 0000000..2c318b2
--- /dev/null
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,Z
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.lur.test;
+
+import static org.junit.Assert.*;
+
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import static org.mockito.Mockito.*;
+
+import org.onap.ccsdk.apps.cadi.lur.LocalPermission;
+import org.onap.ccsdk.apps.cadi.Permission;
+
+public class JU_LocalPermission {
+
+    @Mock
+    Permission perm;
+
+    private LocalPermission localPerm;
+    private String role = "Fake Role";
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(perm.getKey()).thenReturn(role);
+
+        localPerm = new LocalPermission(role);
+    }
+
+    @Test
+    public void getKeyTest() {
+        assertThat(localPerm.getKey(), is(role));
+    }
+
+    @Test
+    public void toStringTest() {
+        assertThat(localPerm.toString(), is(role));
+    }
+
+    @Test
+    public void matchTest() {
+        assertTrue(localPerm.match(perm));
+    }
+
+    @Test
+    public void permTypeTest() {
+        assertThat(localPerm.permType(), is("LOCAL"));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/lur/test/JU_NullLur.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/lur/test/JU_NullLur.java
new file mode 100644 (file)
index 0000000..a993823
--- /dev/null
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,Z
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.lur.test;
+
+import java.security.Principal;
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+import org.junit.*;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import java.lang.reflect.*;
+
+import org.onap.ccsdk.apps.cadi.Permission;
+import org.onap.ccsdk.apps.cadi.lur.NullLur;
+
+public class JU_NullLur {
+
+    @Mock
+    Principal p;
+
+    @Mock
+    Permission perm;
+
+    @Mock
+    List<Permission> perms;
+
+    private NullLur nullLur;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        nullLur = new NullLur();
+    }
+
+    @Test
+    public void coverageTests() throws Exception {
+
+        Field nullClass = NullLur.class.getDeclaredField("NULL");
+        nullClass.setAccessible(true);
+        assertThat(((Permission) nullClass.get(NullLur.class)).permType(), is(""));
+        assertThat(((Permission) nullClass.get(NullLur.class)).getKey(), is(""));
+        assertFalse(((Permission) nullClass.get(NullLur.class)).match(perm));
+
+        nullLur.fishAll(p, perms);
+        nullLur.destroy();
+
+        assertFalse(nullLur.fish(p, perm));
+        assertFalse(nullLur.handlesExclusively(perm));
+        assertFalse(nullLur.handles(p));
+        assertThat(nullLur.createPerm(""), is(nullClass.get(NullLur.class)));
+
+        StringBuilder sb = new StringBuilder();
+        nullLur.clear(p, sb);
+        assertThat(sb.toString(), is("NullLur\n"));
+        assertThat(nullLur.toString(), is("NullLur\n"));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_BasicPrincipal.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_BasicPrincipal.java
new file mode 100644 (file)
index 0000000..d4479c2
--- /dev/null
@@ -0,0 +1,124 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.principal.test;
+
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.mock;
+import org.junit.*;
+
+import java.io.IOException;
+import java.util.Date;
+
+import org.onap.ccsdk.apps.cadi.BasicCred;
+import org.onap.ccsdk.apps.cadi.Symm;
+import org.onap.ccsdk.apps.cadi.principal.BasicPrincipal;
+
+public class JU_BasicPrincipal {
+
+    @Test
+    public void Constructor1Test() throws Exception {
+        // Test that everything works when the content doesn't contain "Basic"
+        BasicPrincipal bp = new BasicPrincipal("content", "domain");
+        assertThat(bp.getName(), is("content"));
+        assertThat(bp.getCred(), is(nullValue()));
+
+        // Test sending a user without an implicit domain
+        String name = "User";
+        String password = "password";
+        String content = name + ":" + password;
+        String domain = "exampledomain.com";
+        String encrypted = new String(Symm.base64.encode(content.getBytes()));
+        bp = new BasicPrincipal("Basic " + encrypted, domain);
+        assertThat(bp.getShortName(), is(name));
+        assertThat(bp.getName(), is(name + "@" + domain));
+        assertThat(bp.getCred(), is(password.getBytes()));
+
+        // Test sending a user with an implicit domain
+        String longName = name + "@" + domain + ":" + password;
+        encrypted = new String(Symm.base64.encode(longName.getBytes()));
+        bp = new BasicPrincipal("Basic " + encrypted, domain);
+        assertThat(bp.getShortName(), is(name));
+        assertThat(bp.getName(), is(name + "@" + domain));
+        assertThat(bp.getCred(), is(password.getBytes()));
+
+        // Check that an exception is throw if no name is given in the content
+        try {
+            bp = new BasicPrincipal("Basic " + new String(Symm.base64.encode("no name".getBytes())), "");
+            fail("Should have thrown an exception");
+        } catch (IOException e) {
+            assertThat(e.getMessage(), is("Invalid Coding"));
+        }
+    }
+
+    @Test
+    public void Constructor2Test() {
+        String name = "User";
+        String password = "password";
+        BasicCred bc = mock(BasicCred.class);
+        when(bc.getUser()).thenReturn(name);
+        when(bc.getCred()).thenReturn(password.getBytes());
+
+        BasicPrincipal bp = new BasicPrincipal(bc, "domain");
+        assertThat(bp.getName(), is(name));
+        assertThat(bp.getCred(), is(password.getBytes()));
+    }
+
+    @Test
+    public void accessorsTest() throws IOException {
+        String name = "User";
+        String password = "password";
+        String content = name + ":" + password;
+        String domain = "exampledomain.com";
+        String encrypted = new String(Symm.base64.encode(content.getBytes()));
+        String bearer = "bearer";
+        long created = System.currentTimeMillis();
+        BasicPrincipal bp = new BasicPrincipal("Basic " + encrypted, domain);
+        bp.setBearer(bearer);
+
+        String expected = "Basic Authorization for " + name + "@" + domain + " evaluated on " + new Date(bp.created()).toString();
+        assertTrue(Math.abs(bp.created() - created) < 10);
+        assertThat(bp.toString(), is(expected));
+        assertThat(bp.tag(), is("BAth"));
+        assertThat(bp.personalName(), is(bp.getName()));
+
+        // This test hits the abstract class BearerPrincipal
+        assertThat(bp.getBearer(), is(bearer));
+    }
+
+
+    @Test
+    public void coverageTest() throws IOException {
+        String name = "User";
+        String password = "password:with:colons";
+        String content = name + ":" + password;
+        String encrypted = new String(Symm.base64.encode(content.getBytes()));
+        @SuppressWarnings("unused")
+        BasicPrincipal bp = new BasicPrincipal("Basic " + encrypted, "domain");
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_CachedBasicPrincipal.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_CachedBasicPrincipal.java
new file mode 100644 (file)
index 0000000..50ec96d
--- /dev/null
@@ -0,0 +1,124 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.principal.test;
+
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.hamcrest.CoreMatchers.is;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+
+import org.onap.ccsdk.apps.cadi.BasicCred;
+import org.onap.ccsdk.apps.cadi.CachedPrincipal;
+import org.onap.ccsdk.apps.cadi.principal.CachedBasicPrincipal;
+import org.onap.ccsdk.apps.cadi.taf.HttpTaf;
+
+public class JU_CachedBasicPrincipal {
+    private Field creatorField;
+    private Field timeToLiveField;
+
+    @Mock
+    private HttpTaf creator;
+
+    private CachedPrincipal.Resp resp;
+
+    @Before
+    public void setup() throws NoSuchFieldException, SecurityException {
+        MockitoAnnotations.initMocks(this);
+
+        creatorField = CachedBasicPrincipal.class.getDeclaredField("creator");
+        timeToLiveField = CachedBasicPrincipal.class.getDeclaredField("timeToLive");
+
+        creatorField.setAccessible(true);
+        timeToLiveField.setAccessible(true);
+    }
+
+    @Test
+    public void Constructor1Test() throws IllegalArgumentException, IllegalAccessException {
+        String name = "User";
+        String password = "password";
+        BasicCred bc = mock(BasicCred.class);
+        when(bc.getUser()).thenReturn(name);
+        when(bc.getCred()).thenReturn(password.getBytes());
+
+        long timeToLive = 10000L;
+        long expires = System.currentTimeMillis() + timeToLive;
+        CachedBasicPrincipal cbp = new CachedBasicPrincipal(creator, bc, "domain", timeToLive);
+
+        assertThat((HttpTaf)creatorField.get(cbp), is(creator));
+        assertThat((Long)timeToLiveField.get(cbp), is(timeToLive));
+        assertTrue(Math.abs(cbp.expires() - expires) < 10);
+    }
+
+    @Test
+    public void Constructor2Test() throws Exception {
+        String name = "User";
+        String password = "password";
+        String content = name + ":" + password;
+        long timeToLive = 10000L;
+        long expires = System.currentTimeMillis() + timeToLive;
+        CachedBasicPrincipal cbp = new CachedBasicPrincipal(creator, content, "domain", timeToLive);
+
+        assertThat((HttpTaf)creatorField.get(cbp), is(creator));
+        assertThat((Long)timeToLiveField.get(cbp), is(timeToLive));
+        assertTrue(Math.abs(cbp.expires() - expires) < 10);
+    }
+
+    @Test
+    public void revalidateTest() throws IOException, IllegalArgumentException, IllegalAccessException, InterruptedException {
+        resp = CachedPrincipal.Resp.REVALIDATED;
+        when(creator.revalidate((CachedPrincipal)any(), any())).thenReturn(resp);
+
+        String name = "User";
+        String password = "password";
+        String content = name + ":" + password;
+        long timeToLive = 10000L;
+        long expires = System.currentTimeMillis() + timeToLive;
+        CachedBasicPrincipal cbp = new CachedBasicPrincipal(creator, content, "domain", timeToLive);
+
+        assertTrue(Math.abs(cbp.expires() - expires) < 10);
+
+        Thread.sleep(1);
+        expires = System.currentTimeMillis() + timeToLive;
+        assertThat(cbp.revalidate(new Object()), is(resp));
+        assertTrue(Math.abs(cbp.expires() - expires) < 10);
+
+        resp = CachedPrincipal.Resp.UNVALIDATED;
+        when(creator.revalidate((CachedPrincipal)any(), any())).thenReturn(resp);
+        expires = System.currentTimeMillis() + timeToLive;
+        cbp = new CachedBasicPrincipal(creator, content, "domain", timeToLive);
+
+        assertThat(cbp.revalidate(new Object()), is(resp));
+        assertTrue(Math.abs(cbp.expires() - expires) < 10);
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_Kind.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_Kind.java
new file mode 100644 (file)
index 0000000..208340a
--- /dev/null
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.principal.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import org.onap.ccsdk.apps.cadi.principal.BasicPrincipal;
+import org.onap.ccsdk.apps.cadi.principal.Kind;
+import org.onap.ccsdk.apps.cadi.principal.OAuth2FormPrincipal;
+import org.onap.ccsdk.apps.cadi.principal.TrustPrincipal;
+import org.onap.ccsdk.apps.cadi.principal.X509Principal;
+
+public class JU_Kind {
+
+    @Mock
+    private TrustPrincipal trust;
+
+    @Mock
+    private X509Principal x509;
+
+    @Mock
+    private OAuth2FormPrincipal oauth;
+
+    @Mock
+    private BasicPrincipal basic;
+
+    @Before
+    public void setup() throws SecurityException {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void getKind() {
+        assertThat(Kind.getKind(trust), is('U'));
+        assertThat(Kind.getKind(x509), is('X'));
+        assertThat(Kind.getKind(oauth), is('O'));
+        assertThat(Kind.getKind(basic), is('B'));
+    }
+
+    @Test
+    public void coverageTest() {
+        @SuppressWarnings("unused")
+        Kind kind = new Kind();
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_OAuth2FormPrincipal.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_OAuth2FormPrincipal.java
new file mode 100644 (file)
index 0000000..180f779
--- /dev/null
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.principal.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import org.onap.ccsdk.apps.cadi.principal.OAuth2FormPrincipal;
+
+public class JU_OAuth2FormPrincipal {
+
+    private String username = "user";
+    private String id = "id";
+
+    @Test
+    public void accessorsTest() {
+        OAuth2FormPrincipal oauth = new OAuth2FormPrincipal(id, username);
+        assertThat(oauth.getName(), is(username));
+        assertThat(oauth.client_id(), is(id));
+        assertThat(oauth.tag(), is("OAuth"));
+    }
+
+    @Test
+    public void personalNameTest() {
+        OAuth2FormPrincipal oauth = new OAuth2FormPrincipal(id, username);
+        assertThat(oauth.personalName(), is(username + "|" + id));
+
+        oauth = new OAuth2FormPrincipal(id, null);
+        assertThat(oauth.personalName(), is(id));
+
+        oauth = new OAuth2FormPrincipal(id, id);
+        assertThat(oauth.personalName(), is(id));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_StringTagLookup.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_StringTagLookup.java
new file mode 100644 (file)
index 0000000..8e9656c
--- /dev/null
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.principal.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import org.onap.ccsdk.apps.cadi.principal.StringTagLookup;
+
+public class JU_StringTagLookup {
+
+    @Test
+    public void accessorsTest() throws Exception {
+        String tag = "tag";
+        StringTagLookup stl = new StringTagLookup(tag);
+        assertThat(stl.lookup(), is(tag));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_TaggedPrincipal.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_TaggedPrincipal.java
new file mode 100644 (file)
index 0000000..fe58f7d
--- /dev/null
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.principal.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import org.onap.ccsdk.apps.cadi.principal.TaggedPrincipal;
+import org.onap.ccsdk.apps.cadi.principal.TaggedPrincipal.TagLookup;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.principal.StringTagLookup;
+
+public class JU_TaggedPrincipal {
+
+    private final String name = "stubbedName";
+    private final String tag = "tag";
+
+    private class TaggedPrincipalStub extends TaggedPrincipal {
+        public TaggedPrincipalStub() { super(); }
+        public TaggedPrincipalStub(final TagLookup tl) { super(tl); }
+        @Override public String getName() { return name; }
+        @Override public String tag() { return null; }
+    }
+
+    private class WhinyTagLookup implements TagLookup {
+        public WhinyTagLookup(final String tag) { }
+        @Override
+        public String lookup() throws CadiException {
+            throw new CadiException();
+        }
+    }
+
+    @Test
+    public void personalNameTest() {
+        TaggedPrincipal tp = new TaggedPrincipalStub();
+        assertThat(tp.personalName(), is(name));
+
+        StringTagLookup stl = new StringTagLookup(tag);
+        tp = new TaggedPrincipalStub(stl);
+        assertThat(tp.personalName(), is(tag));
+
+        WhinyTagLookup wtl = new WhinyTagLookup(tag);
+        tp.setTagLookup(wtl);
+        assertThat(tp.personalName(), is(name));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_TrustPrincipal.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_TrustPrincipal.java
new file mode 100644 (file)
index 0000000..d47f761
--- /dev/null
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.principal.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import java.security.Principal;
+
+import org.onap.ccsdk.apps.cadi.UserChain;
+import org.onap.ccsdk.apps.cadi.principal.TaggedPrincipal;
+import org.onap.ccsdk.apps.cadi.principal.TrustPrincipal;
+
+public class JU_TrustPrincipal {
+
+    private final String ucName = "UserChain";
+    private final String uc = "This is a UserChain";
+    private final String taggedName = "TaggedPrincipal";
+    private final String tag = "tag";
+    private final String pName = "Principal";
+
+    private class UserChainPrincipalStub implements Principal, UserChain {
+        @Override public String userChain() { return uc; }
+        @Override public String getName() { return ucName; }
+    }
+
+    private class TaggedPrincipalStub extends TaggedPrincipal {
+        public TaggedPrincipalStub() { super(); }
+        @Override public String getName() { return taggedName; }
+        @Override public String tag() { return tag; }
+    }
+
+    private class PrincipalStub implements Principal {
+        @Override public String getName() { return pName; }
+    }
+
+    @Test
+    public void userChainConstructorTest() {
+        UserChainPrincipalStub ucps = new UserChainPrincipalStub();
+        TrustPrincipal tp = new TrustPrincipal(ucps, taggedName);
+        assertThat(tp.getName(), is(taggedName));
+        assertThat(tp.userChain(), is(uc));
+        assertSame(tp.original(), ucps);
+        assertThat(tp.tag(), is(uc));
+        assertThat(tp.personalName(), is(ucName + '[' + uc + ']'));
+    }
+
+    @Test
+    public void taggedPrincipalConstructorTest() {
+        TaggedPrincipal tagged = new TaggedPrincipalStub();
+        TrustPrincipal tp = new TrustPrincipal(tagged, taggedName);
+        assertThat(tp.getName(), is(taggedName));
+        assertThat(tp.userChain(), is(tag));
+        assertSame(tp.original(), tagged);
+        assertThat(tp.tag(), is(tag));
+        assertThat(tp.personalName(), is(taggedName + '[' + tag + ']'));
+    }
+
+    @Test
+    public void principalConstructorTest() {
+        Principal principal = new PrincipalStub();
+        TrustPrincipal tp = new TrustPrincipal(principal, pName);
+        assertThat(tp.getName(), is(pName));
+        assertThat(tp.userChain(), is(principal.getClass().getSimpleName()));
+        assertSame(tp.original(), principal);
+        assertThat(tp.tag(), is(principal.getClass().getSimpleName()));
+        assertThat(tp.personalName(), is(pName + '[' + principal.getClass().getSimpleName() + ']'));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_UnAuthPrincipal.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_UnAuthPrincipal.java
new file mode 100644 (file)
index 0000000..4eccf27
--- /dev/null
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.principal.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import org.onap.ccsdk.apps.cadi.principal.UnAuthPrincipal;
+
+public class JU_UnAuthPrincipal {
+
+    private final String name = "name";
+
+    @Test
+    public void accessorsTest() {
+        UnAuthPrincipal up = new UnAuthPrincipal(name);
+        assertThat(up.getName(), is(name));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_X509Principal.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/principal/test/JU_X509Principal.java
new file mode 100644 (file)
index 0000000..47ec43b
--- /dev/null
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.principal.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import static org.mockito.Mockito.*;
+import org.junit.*;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+
+import org.onap.ccsdk.apps.cadi.principal.X509Principal;
+
+public class JU_X509Principal {
+
+    private final String name = "x509 name";
+    private final byte[] cred = "super duper secret password".getBytes();
+
+    @Mock
+    X509Certificate cert;
+
+    @Mock
+    Principal subject;
+
+    @Before
+    public void setup() throws CertificateEncodingException {
+        MockitoAnnotations.initMocks(this);
+        when(cert.getEncoded()).thenReturn(cred);
+    }
+
+    @Test
+    public void constructor1Test() throws IOException {
+        X509Principal x509 = new X509Principal(name, cert);
+        // Call twice to hit both branches
+        assertThat(x509.getAsHeader(), is("X509 " + cred));
+        assertThat(x509.getAsHeader(), is("X509 " + cred));
+        assertThat(x509.toString(), is("X509 Authentication for " + name));
+        assertTrue(x509.getCred().equals(cred));
+        assertThat(x509.getName(), is(name));
+        assertThat(x509.tag(), is("x509"));
+    }
+
+    @Test
+    public void constructor2Test() throws IOException {
+        X509Principal x509 = new X509Principal(name, cert, cred,null);
+        // Call twice to hit both branches
+        assertThat(x509.getAsHeader(), is("X509 " + cred));
+        assertThat(x509.toString(), is("X509 Authentication for " + name));
+        assertTrue(x509.getCred().equals(cred));
+        assertThat(x509.getName(), is(name));
+        assertThat(x509.tag(), is("x509"));
+    }
+
+    @Test
+    public void constructor3Test() throws IOException {
+        final String longName = "name@domain";
+        when(subject.getName()).thenReturn("OU=" + longName + ",extra");
+        when(cert.getSubjectDN()).thenReturn(subject);
+        X509Principal x509 = new X509Principal(cert, cred,null);
+        // Call twice to hit both branches
+        assertThat(x509.getAsHeader(), is("X509 " + cred));
+        assertThat(x509.toString(), is("X509 Authentication for " + longName));
+        assertTrue(x509.getCred().equals(cred));
+        assertThat(x509.getName(), is(longName));
+
+        when(subject.getName()).thenReturn(longName + ",extra");
+        when(cert.getSubjectDN()).thenReturn(subject);
+        try {
+            x509 = new X509Principal(cert, cred, null);
+            fail("Should have thrown an Exception");
+        } catch (IOException e) {
+            assertThat(e.getMessage(), is("X509 does not have Identity as CN"));
+        }
+
+        when(subject.getName()).thenReturn("OU=" + longName);
+        when(cert.getSubjectDN()).thenReturn(subject);
+        try {
+            x509 = new X509Principal(cert, cred, null);
+            fail("Should have thrown an Exception");
+        } catch (IOException e) {
+            assertThat(e.getMessage(), is("X509 does not have Identity as CN"));
+        }
+
+        when(subject.getName()).thenReturn("OU=" + name + ",exta");
+        when(cert.getSubjectDN()).thenReturn(subject);
+        try {
+            x509 = new X509Principal(cert, cred, null);
+            fail("Should have thrown an Exception");
+        } catch (IOException e) {
+            assertThat(e.getMessage(), is("X509 does not have Identity as CN"));
+        }
+
+    }
+
+    @Test
+    public void throwsTest() throws CertificateEncodingException {
+        when(cert.getEncoded()).thenThrow(new CertificateEncodingException());
+        X509Principal x509 = new X509Principal(name, cert);
+        assertThat(x509.getCred(), is(nullValue()));
+        try {
+            x509.getAsHeader();
+            fail("Should have thrown an Exception");
+        } catch (IOException e) {
+        }
+    }
+
+    @Test
+    public void getCredTest() {
+        X509Principal x509 = new X509Principal(name, cert);
+        // Call twice to hit both branches
+        assertTrue(x509.getCred().equals(cred));
+        assertTrue(x509.getCred().equals(cred));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/basic/test/JU_BasicHttpTaf.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/basic/test/JU_BasicHttpTaf.java
new file mode 100644 (file)
index 0000000..0e75ea3
--- /dev/null
@@ -0,0 +1,213 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf.basic.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.when;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.security.Principal;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.Map;
+
+import jakarta.servlet.AsyncContext;
+import jakarta.servlet.DispatcherType;
+import jakarta.servlet.RequestDispatcher;
+import jakarta.servlet.ServletConnection;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletInputStream;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpSession;
+import jakarta.servlet.http.HttpUpgradeHandler;
+import jakarta.servlet.http.Part;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.ccsdk.apps.cadi.BasicCred;
+import org.onap.ccsdk.apps.cadi.CachedPrincipal;
+import org.onap.ccsdk.apps.cadi.CachedPrincipal.Resp;
+import org.onap.ccsdk.apps.cadi.CredVal;
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.Symm;
+import org.onap.ccsdk.apps.cadi.Taf.LifeForm;
+import org.onap.ccsdk.apps.cadi.taf.basic.BasicHttpTaf;
+
+public class JU_BasicHttpTaf {
+
+    private final static String realm = "realm";
+    private final static String id = "id";
+    private final static String addr = "addr";
+
+    private final static String name = "User";
+    private final static String password = "password";
+    private final static String content = name + ":" + password;
+    private static String encrypted;
+
+    private final static long timeToLive = 10000L;
+
+    private PropAccess access;
+
+    @Mock private HttpServletResponse respMock;
+    @Mock private HttpServletRequest reqMock;
+    @Mock private CredVal rbacMock;
+    @Mock private CachedPrincipal princMock;
+
+    @Before
+    public void setup() throws IOException {
+        MockitoAnnotations.initMocks(this);
+        access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]);
+        encrypted = new String(Symm.base64.encode(content.getBytes()));
+    }
+
+    @Test
+    public void test() {
+        BasicHttpTaf taf = new BasicHttpTaf(access, rbacMock, realm, timeToLive, true);
+        BasicCredStub bcstub = new BasicCredStub();
+        assertThat(taf.validate(LifeForm.SBLF, bcstub, respMock), is(not(nullValue())));
+
+        assertThat(taf.validate(LifeForm.SBLF, reqMock, respMock), is(not(nullValue())));
+
+        when(reqMock.getHeader("Authorization")).thenReturn("test");
+        assertThat(taf.validate(LifeForm.SBLF, reqMock, respMock), is(not(nullValue())));
+
+        when(reqMock.getHeader("Authorization")).thenReturn("Basic " + encrypted);
+        assertThat(taf.validate(LifeForm.SBLF, reqMock, respMock), is(not(nullValue())));
+
+        assertThat(taf.revalidate(princMock, "state"), is(Resp.NOT_MINE));
+
+        assertThat(taf.toString(), is("Basic Auth enabled on realm: " + realm));
+    }
+
+    private class BasicCredStub implements HttpServletRequest, BasicCred {
+        @Override public String getUser() { return id; }
+        @Override public String getRemoteAddr() { return addr; }
+
+        @Override public AsyncContext getAsyncContext() { return null; }
+        @Override public Object getAttribute(String arg0) { return null; }
+        @Override public Enumeration<String> getAttributeNames() { return null; }
+        @Override public String getCharacterEncoding() { return null; }
+        @Override public int getContentLength() { return 0; }
+        @Override public String getContentType() { return null; }
+        @Override public DispatcherType getDispatcherType() { return null; }
+        @Override public ServletInputStream getInputStream() throws IOException { return null; }
+        @Override public String getLocalAddr() { return null; }
+        @Override public String getLocalName() { return null; }
+        @Override public int getLocalPort() { return 0; }
+        @Override public Locale getLocale() { return null; }
+        @Override public Enumeration<Locale> getLocales() { return null; }
+        @Override public String getParameter(String arg0) { return null; }
+        @Override public Map<String, String[]> getParameterMap() { return null; }
+        @Override public Enumeration<String> getParameterNames() { return null; }
+        @Override public String[] getParameterValues(String arg0) { return null; }
+        @Override public String getProtocol() { return null; }
+        @Override public BufferedReader getReader() throws IOException { return null; }
+
+        @Override public String getRemoteHost() { return null; }
+        @Override public int getRemotePort() { return 0; }
+        @Override public RequestDispatcher getRequestDispatcher(String arg0) { return null; }
+        @Override public String getScheme() { return null; }
+        @Override public String getServerName() { return null; }
+        @Override public int getServerPort() { return 0; }
+        @Override public ServletContext getServletContext() { return null; }
+        @Override public boolean isAsyncStarted() { return false; }
+        @Override public boolean isAsyncSupported() { return false; }
+        @Override public boolean isSecure() { return false; }
+        @Override public void removeAttribute(String arg0) { }
+        @Override public void setAttribute(String arg0, Object arg1) { }
+        @Override public void setCharacterEncoding(String arg0) throws UnsupportedEncodingException { }
+        @Override public AsyncContext startAsync() throws IllegalStateException { return null; }
+        @Override public AsyncContext startAsync(ServletRequest arg0, ServletResponse arg1) throws IllegalStateException { return null; }
+        @Override public byte[] getCred() { return null; }
+        @Override public void setUser(String user) { }
+        @Override public void setCred(byte[] passwd) { }
+        @Override public boolean authenticate(HttpServletResponse arg0) throws IOException, ServletException { return false; }
+        @Override public String getAuthType() { return null; }
+        @Override public String getContextPath() { return null; }
+        @Override public Cookie[] getCookies() { return null; }
+        @Override public long getDateHeader(String arg0) { return 0; }
+        @Override public String getHeader(String arg0) { return null; }
+        @Override public Enumeration<String> getHeaderNames() { return null; }
+        @Override public Enumeration<String> getHeaders(String arg0) { return null; }
+        @Override public int getIntHeader(String arg0) { return 0; }
+        @Override public String getMethod() { return null; }
+        @Override public Part getPart(String arg0) throws IOException, ServletException { return null; }
+        @Override public Collection<Part> getParts() throws IOException, ServletException { return null; }
+        @Override public String getPathInfo() { return null; }
+        @Override public String getPathTranslated() { return null; }
+        @Override public String getQueryString() { return null; }
+        @Override public String getRemoteUser() { return null; }
+        @Override public String getRequestURI() { return null; }
+        @Override public StringBuffer getRequestURL() { return null; }
+        @Override public String getRequestedSessionId() { return null; }
+        @Override public String getServletPath() { return null; }
+        @Override public HttpSession getSession() { return null; }
+        @Override public HttpSession getSession(boolean arg0) { return null; }
+        @Override public Principal getUserPrincipal() { return null; }
+        @Override public boolean isRequestedSessionIdFromCookie() { return false; }
+        @Override public boolean isRequestedSessionIdFromURL() { return false; }
+        @Override public boolean isRequestedSessionIdValid() { return false; }
+        @Override public boolean isUserInRole(String arg0) { return false; }
+        @Override public void login(String arg0, String arg1) throws ServletException { }
+        @Override public void logout() throws ServletException { }
+        @Override
+        public long getContentLengthLong() {
+          return 0L;
+        }
+        @Override
+        public String getRequestId() {
+            return null;
+        }
+        @Override
+        public String getProtocolRequestId() {
+            return null;
+        }
+        @Override
+        public ServletConnection getServletConnection() {
+            return null;
+        }
+        @Override
+        public String changeSessionId() {
+            return null;
+        }
+        @Override
+        public <T extends HttpUpgradeHandler> T upgrade(Class<T> handlerClass) throws IOException, ServletException {
+
+            throw new UnsupportedOperationException("Unimplemented method 'upgrade'");
+        }
+    }
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/basic/test/JU_BasicHttpTafResp.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/basic/test/JU_BasicHttpTafResp.java
new file mode 100644 (file)
index 0000000..b96738d
--- /dev/null
@@ -0,0 +1,67 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf.basic.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.principal.TaggedPrincipal;
+import org.onap.ccsdk.apps.cadi.taf.TafResp.RESP;
+import org.onap.ccsdk.apps.cadi.taf.basic.BasicHttpTafResp;
+
+public class JU_BasicHttpTafResp {
+
+    private final static String realm = "realm";
+    private final static String description = "description";
+
+    private PropAccess access;
+
+    @Mock private HttpServletResponse respMock;
+    @Mock private TaggedPrincipal princMock;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]);
+    }
+
+    @Test
+    public void test() throws IOException {
+        BasicHttpTafResp tafResp = new BasicHttpTafResp(access, princMock, description, RESP.IS_AUTHENTICATED, respMock, realm, false);
+
+        assertThat(tafResp.authenticate(), is(RESP.HTTP_REDIRECT_INVOKED));
+        assertThat(tafResp.isAuthenticated(), is (RESP.IS_AUTHENTICATED));
+        assertThat(tafResp.isFailedAttempt(), is(false));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/cert/test/JU_X509HttpTafResp.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/cert/test/JU_X509HttpTafResp.java
new file mode 100644 (file)
index 0000000..a703a42
--- /dev/null
@@ -0,0 +1,63 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf.cert.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.principal.TaggedPrincipal;
+import org.onap.ccsdk.apps.cadi.taf.TafResp.RESP;
+import org.onap.ccsdk.apps.cadi.taf.cert.X509HttpTafResp;
+
+public class JU_X509HttpTafResp {
+
+    private final static String description = "description";
+    private final static RESP status = RESP.IS_AUTHENTICATED;
+
+    private PropAccess access;
+
+    @Mock private TaggedPrincipal princMock;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]);
+    }
+
+    @Test
+    public void test() throws IOException {
+        X509HttpTafResp resp = new X509HttpTafResp(access, princMock, description, status);
+        assertThat(resp.authenticate(), is(RESP.TRY_ANOTHER_TAF));
+        assertThat(resp.isAuthenticated(), is(status));
+        assertThat(resp.toString(), is(status.name()));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/dos/test/JU_DenialOfServiceTaf.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/dos/test/JU_DenialOfServiceTaf.java
new file mode 100644 (file)
index 0000000..4dc12c4
--- /dev/null
@@ -0,0 +1,372 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.taf.dos.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import static org.mockito.Mockito.*;
+import org.junit.*;
+import org.mockito.*;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.List;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.CachedPrincipal.Resp;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.config.Config;
+import org.onap.ccsdk.apps.cadi.taf.TafResp;
+import org.onap.ccsdk.apps.cadi.Taf.LifeForm;
+import org.onap.ccsdk.apps.cadi.taf.dos.DenialOfServiceTaf;
+import org.onap.ccsdk.apps.cadi.taf.dos.DenialOfServiceTaf.Counter;
+
+public class JU_DenialOfServiceTaf {
+
+    @Mock
+    HttpServletResponse respMock;
+
+    @Mock
+    HttpServletRequest reqMock1;
+
+    @Mock
+    HttpServletRequest reqMock2;
+
+    @Mock
+    HttpServletRequest reqMock3;
+
+    @Mock
+    Access accessMock;
+
+    private File dosIPFile;
+    private File dosIDFile;
+    private File dosDir;
+    private final String dosDirName = "test";
+
+    private final String id1 = "id1";
+    private final String id2 = "id2";
+
+    private final String ip1 = "111.111.111.111";
+    private final String ip2 = "222.222.222.222";
+
+    @Before
+    public void setup() throws IOException {
+        MockitoAnnotations.initMocks(this);
+
+        dosDir = new File(dosDirName);
+        dosDir.mkdirs();
+        dosIPFile = new File(dosDirName, "/dosIP");
+        dosIDFile = new File(dosDirName, "/dosID");
+        dosIPFile.delete();
+        dosIDFile.delete();
+
+        when(accessMock.getProperty(Config.AAF_DATA_DIR, null)).thenReturn(dosDirName);
+        when(reqMock1.getRemoteAddr()).thenReturn(ip1);
+        when(reqMock2.getRemoteAddr()).thenReturn(ip2);
+
+        setPrivateField(DenialOfServiceTaf.class, "deniedIP", null);
+        setPrivateField(DenialOfServiceTaf.class, "deniedID", null);
+        setPrivateField(DenialOfServiceTaf.class, "dosIP", null);
+        setPrivateField(DenialOfServiceTaf.class, "dosID", null);
+    }
+
+    @After
+    public void tearDown() {
+        dosIPFile = new File(dosDirName, "/dosIP");
+        dosIDFile = new File(dosDirName, "/dosID");
+        dosIPFile.delete();
+        dosIDFile.delete();
+    }
+
+    @Test
+    public void constructorTest() throws CadiException {
+        @SuppressWarnings("unused")
+        DenialOfServiceTaf dost;
+
+        // coverage...
+        when(accessMock.getProperty(Config.AAF_DATA_DIR, null)).thenReturn(null);
+        dost = new DenialOfServiceTaf(accessMock);
+
+        when(accessMock.getProperty(Config.AAF_DATA_DIR, null)).thenReturn(dosDirName);
+        dost = new DenialOfServiceTaf(accessMock);
+
+        // more coverage...
+        dost = new DenialOfServiceTaf(accessMock);
+
+        // more coverage...
+        setPrivateField(DenialOfServiceTaf.class, "dosID", null);
+        dost = new DenialOfServiceTaf(accessMock);
+    }
+
+    @Test
+    public void validateTest() throws CadiException {
+        DenialOfServiceTaf dost;
+        TafResp tafResp;
+
+        dost = new DenialOfServiceTaf(accessMock);
+        tafResp = dost.validate(LifeForm.SBLF, reqMock1, respMock);
+
+        assertThat(tafResp.desc(), is("Not processing this transaction: This Transaction is not denied"));
+        assertThat(tafResp.taf(), is("DenialOfServiceTaf"));
+
+        assertThat(DenialOfServiceTaf.denyIP(ip1), is(true));
+
+        tafResp = dost.validate(LifeForm.SBLF, reqMock1, respMock);
+        assertThat(tafResp.desc(), is(ip1 + " is on the IP Denial list"));
+
+        tafResp = dost.validate(LifeForm.SBLF, reqMock2, respMock);
+        assertThat(tafResp.desc(), is("Not processing this transaction: This Transaction is not denied"));
+        assertThat(tafResp.taf(), is("DenialOfServiceTaf"));
+    }
+
+    @Test
+    public void revalidateTest() throws CadiException {
+        DenialOfServiceTaf dost = new DenialOfServiceTaf(accessMock);
+        Resp resp = dost.revalidate(null, null);
+        assertThat(resp, is(Resp.NOT_MINE));
+    }
+
+    @Test
+    public void denyIPTest() throws CadiException {
+        assertThat(DenialOfServiceTaf.isDeniedIP(ip1), is(nullValue()));
+        assertThat(DenialOfServiceTaf.denyIP(ip1), is(true));  // true because it's been added
+        assertThat(DenialOfServiceTaf.denyIP(ip2), is(true));  // true because it's been added
+        assertThat(DenialOfServiceTaf.denyIP(ip1), is(false)); // false because it's already been added
+        assertThat(DenialOfServiceTaf.denyIP(ip2), is(false)); // false because it's already been added
+
+        Counter counter;
+        counter = DenialOfServiceTaf.isDeniedIP(ip1);
+        assertThat(counter.getName(), is(ip1));
+        assertThat(counter.getCount(), is(0));
+        assertThat(counter.getLast(), is(0L));
+        assertThat(counter.toString(), is(ip1 + " is on the denied list, but has not attempted Access" ));
+
+        DenialOfServiceTaf dost = new DenialOfServiceTaf(accessMock);
+        dost.validate(LifeForm.SBLF, reqMock1, respMock);
+        long approxTime = System.currentTimeMillis();
+
+        counter = DenialOfServiceTaf.isDeniedIP(ip1);
+        assertThat(counter.getName(), is(ip1));
+        assertThat(counter.getCount(), is(1));
+        assertThat((Math.abs(approxTime - counter.getLast()) < 10), is(true));
+        assertThat(counter.toString().contains(ip1), is(true));
+        assertThat(counter.toString().contains(" has been denied 1 times since "), is(true));
+        assertThat(counter.toString().contains(".  Last denial was "), is(true));
+
+        // coverage...
+        dost.validate(LifeForm.SBLF, reqMock1, respMock);
+
+        assertThat(DenialOfServiceTaf.removeDenyIP(ip1), is(true));
+        assertThat(DenialOfServiceTaf.removeDenyIP(ip1), is(false));
+        assertThat(DenialOfServiceTaf.removeDenyIP(ip2), is(true));
+        assertThat(DenialOfServiceTaf.removeDenyIP(ip2), is(false));
+    }
+
+    @Test
+    public void denyIDTest() throws CadiException {
+        assertThat(DenialOfServiceTaf.isDeniedID(id1), is(nullValue()));
+        assertThat(DenialOfServiceTaf.denyID(id1), is(true));  // true because it's been added
+        assertThat(DenialOfServiceTaf.denyID(id2), is(true));  // true because it's been added
+        assertThat(DenialOfServiceTaf.denyID(id1), is(false)); // false because it's already been added
+        assertThat(DenialOfServiceTaf.denyID(id2), is(false)); // false because it's already been added
+
+        Counter counter;
+        counter = DenialOfServiceTaf.isDeniedID(id1);
+        assertThat(counter.getName(), is(id1));
+        assertThat(counter.getCount(), is(0));
+        assertThat(counter.getLast(), is(0L));
+
+        assertThat(DenialOfServiceTaf.removeDenyID(id1), is(true));
+        assertThat(DenialOfServiceTaf.removeDenyID(id1), is(false));
+        assertThat(DenialOfServiceTaf.removeDenyID(id2), is(true));
+        assertThat(DenialOfServiceTaf.removeDenyID(id2), is(false));
+    }
+
+    @Test
+    public void reportTest() throws CadiException {
+        DenialOfServiceTaf dost = new DenialOfServiceTaf(accessMock);
+        List<String> denials = dost.report();
+        assertThat(denials.size(), is(0));
+
+        DenialOfServiceTaf.denyID(id1);
+        DenialOfServiceTaf.denyID(id2);
+
+        DenialOfServiceTaf.denyIP(ip1);
+        DenialOfServiceTaf.denyIP(ip2);
+
+        denials = dost.report();
+        assertThat(denials.size(), is(4));
+        for (String denied : denials) {
+            switch (denied.split(" ", 2)[0]) {
+                case ip1:
+                case ip2:
+                case id1:
+                case id2:
+                    break;
+                default:
+                    fail("The line: [" + denied + "] shouldn't be in the report");
+            }
+        }
+    }
+
+    @Test
+    public void respDenyIDTest() {
+        TafResp tafResp = DenialOfServiceTaf.respDenyID(accessMock, id1);
+        assertThat(tafResp.desc(), is(id1 + " is on the Identity Denial list"));
+    }
+
+    @Test
+    public void ipFileIOTest() throws CadiException, IOException {
+        @SuppressWarnings("unused")
+        DenialOfServiceTaf dost;
+
+        dosIPFile.createNewFile();
+
+        // coverage...
+        DenialOfServiceTaf.denyIP(ip1);
+        DenialOfServiceTaf.removeDenyIP(ip1);
+
+        dost = new DenialOfServiceTaf(accessMock);
+        DenialOfServiceTaf.denyIP(ip1);
+        DenialOfServiceTaf.denyIP(ip2);
+        // coverage...
+        DenialOfServiceTaf.denyIP(ip2);
+
+        String contents = readContentsFromFile(dosIPFile);
+        assertThat(contents.contains(ip1), is(true));
+        assertThat(contents.contains(ip2), is(true));
+
+        // Removing all ips should delete the file
+        assertThat(dosIPFile.exists(), is(true));
+        DenialOfServiceTaf.removeDenyIP(ip1);
+        DenialOfServiceTaf.removeDenyIP(ip2);
+        assertThat(dosIPFile.exists(), is(false));
+
+        dosIPFile.createNewFile();
+
+        DenialOfServiceTaf.denyIP(ip1);
+        DenialOfServiceTaf.denyIP(ip2);
+
+        setPrivateField(DenialOfServiceTaf.class, "dosIP", null);
+        dost = new DenialOfServiceTaf(accessMock);
+
+        contents = readContentsFromFile(dosIPFile);
+        assertThat(contents.contains(ip1), is(true));
+        assertThat(contents.contains(ip2), is(true));
+
+        dosIPFile.delete();
+
+        // coverage...
+        setPrivateField(DenialOfServiceTaf.class, "deniedIP", null);
+        DenialOfServiceTaf.denyIP(ip1);
+        dosIPFile.delete();
+        DenialOfServiceTaf.removeDenyIP(ip1);
+
+        // coverage...
+        dosIPFile.delete();
+        setPrivateField(DenialOfServiceTaf.class, "dosIP", null);
+        dost = new DenialOfServiceTaf(accessMock);
+    }
+
+    @Test
+    public void idFileIOTest() throws CadiException, IOException {
+        @SuppressWarnings("unused")
+        DenialOfServiceTaf dost;
+
+        dosIDFile.createNewFile();
+
+        // coverage...
+        DenialOfServiceTaf.denyID(id1);
+        DenialOfServiceTaf.removeDenyID(id1);
+
+        dost = new DenialOfServiceTaf(accessMock);
+        DenialOfServiceTaf.denyID(id1);
+        DenialOfServiceTaf.denyID(id2);
+        // coverage...
+        DenialOfServiceTaf.denyID(id2);
+
+        String contents = readContentsFromFile(dosIDFile);
+        assertThat(contents.contains(id1), is(true));
+        assertThat(contents.contains(id2), is(true));
+
+        // Removing all ids should delete the file
+        assertThat(dosIDFile.exists(), is(true));
+        DenialOfServiceTaf.removeDenyID(id1);
+        DenialOfServiceTaf.removeDenyID(id2);
+        assertThat(dosIDFile.exists(), is(false));
+
+        dosIDFile.createNewFile();
+
+        DenialOfServiceTaf.denyID(id1);
+        DenialOfServiceTaf.denyID(id2);
+
+        setPrivateField(DenialOfServiceTaf.class, "dosID", null);
+        dost = new DenialOfServiceTaf(accessMock);
+
+        contents = readContentsFromFile(dosIDFile);
+        assertThat(contents.contains(id1), is(true));
+        assertThat(contents.contains(id2), is(true));
+
+        dosIDFile.delete();
+
+        // coverage...
+        setPrivateField(DenialOfServiceTaf.class, "deniedID", null);
+        DenialOfServiceTaf.denyID(id1);
+        dosIDFile.delete();
+        DenialOfServiceTaf.removeDenyID(id1);
+
+        // coverage...
+        dosIDFile.delete();
+        setPrivateField(DenialOfServiceTaf.class, "dosID", null);
+        dost = new DenialOfServiceTaf(accessMock);
+    }
+
+    private void setPrivateField(Class<?> clazz, String fieldName, Object value) {
+        try {
+            Field field = clazz.getDeclaredField(fieldName);
+            field.setAccessible(true);
+            field.set(null, value);
+            field.setAccessible(false);
+        } catch (Exception e) {
+            System.err.println("Could not set field [" + fieldName + "] to " + value);
+        }
+    }
+
+    private String readContentsFromFile(File file) throws IOException {
+        BufferedReader br = new BufferedReader(new FileReader(file));
+        StringBuilder sb = new StringBuilder();
+        String line;
+        while ((line = br.readLine()) != null) {
+            sb.append(line);
+        }
+        br.close();
+        return sb.toString();
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/dos/test/JU_DenialOfServiceTafResp.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/dos/test/JU_DenialOfServiceTafResp.java
new file mode 100644 (file)
index 0000000..7075264
--- /dev/null
@@ -0,0 +1,57 @@
+/**
+ *
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf.dos.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.taf.TafResp.RESP;
+import org.onap.ccsdk.apps.cadi.taf.dos.DenialOfServiceTafResp;
+
+public class JU_DenialOfServiceTafResp {
+
+    private final static String description = "description";
+    private final static RESP status = RESP.IS_AUTHENTICATED;
+
+    private PropAccess access;
+
+    @Before
+    public void setup() {
+        access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]);
+    }
+
+    @Test
+    public void test() throws IOException {
+        DenialOfServiceTafResp resp = new DenialOfServiceTafResp(access, status, description);
+        assertThat(resp.isAuthenticated(), is(status));
+        assertThat(resp.authenticate(), is(status));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/test/JU_AbsTafResp.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/test/JU_AbsTafResp.java
new file mode 100644 (file)
index 0000000..97696e3
--- /dev/null
@@ -0,0 +1,91 @@
+/*******************************************************************************
+* ============LICENSE_START====================================================
+* * org.onap.ccsdk
+* * ===========================================================================
+* * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+* * ===========================================================================
+* * Licensed under the Apache License, Version 2.0 (the "License");
+* * you may not use this file except in compliance with the License.
+* * You may obtain a copy of the License at
+* *
+*  *      http://www.apache.org/licenses/LICENSE-2.0
+* *
+*  * Unless required by applicable law or agreed to in writing, software
+* * distributed under the License is distributed on an "AS IS" BASIS,
+* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* * See the License for the specific language governing permissions and
+* * limitations under the License.
+* * ============LICENSE_END====================================================
+* *
+* *
+******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.taf.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.principal.TaggedPrincipal;
+import org.onap.ccsdk.apps.cadi.taf.AbsTafResp;
+import org.onap.ccsdk.apps.cadi.taf.TafResp.RESP;
+
+public class JU_AbsTafResp {
+
+    private static final String JUNIT = "Junit";
+    private static final String name = "name";
+    private static final String tag = "tag";
+    private static final String description = "description";
+
+    private Access access;
+    private TaggedPrincipal taggedPrinc;
+
+    @Before
+    public void setup() {
+        access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]);
+        taggedPrinc = new TaggedPrincipal() {
+            @Override public String getName() { return name; }
+            @Override public String tag() { return tag; }
+        };
+    }
+
+    @Test
+    public void test() {
+        AbsTafResp tafResp = new AbsTafResp(access, JUNIT, taggedPrinc, description) {
+            @Override public RESP authenticate() throws IOException {
+                return null;
+            }
+        };
+
+        assertThat(tafResp.isValid(), is(true));
+        assertThat(tafResp.desc(), is(description));
+        assertThat(tafResp.taf(), is(JUNIT));
+        assertThat(tafResp.isAuthenticated(), is(RESP.IS_AUTHENTICATED));
+        assertThat(tafResp.getPrincipal(), is(taggedPrinc));
+        assertThat(tafResp.getAccess(), is(access));
+        assertThat(tafResp.isFailedAttempt(), is(false));
+
+        tafResp = new AbsTafResp(null, JUNIT, "unknown", null) {
+            @Override public RESP authenticate() throws IOException {
+                return null;
+            }
+        };
+
+        assertThat(tafResp.isValid(), is(false));
+        assertThat(tafResp.isAuthenticated(), is(RESP.TRY_ANOTHER_TAF));
+        assertThat(tafResp.getPrincipal(), is(nullValue()));
+        assertThat(tafResp.getTarget(), is("unknown"));
+        assertThat(tafResp.getAccess(), is(nullValue()));
+        assertThat(tafResp.taf(), is(JUNIT));
+        assertThat(tafResp.isFailedAttempt(), is(false));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/test/JU_EpiTaf.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/test/JU_EpiTaf.java
new file mode 100644 (file)
index 0000000..fc93811
--- /dev/null
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.taf.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import java.io.IOException;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.Taf;
+import org.onap.ccsdk.apps.cadi.taf.TafResp;
+import org.onap.ccsdk.apps.cadi.taf.TafResp.RESP;
+
+import org.onap.ccsdk.apps.cadi.taf.EpiTaf;
+import org.onap.ccsdk.apps.cadi.taf.NullTaf;
+import org.onap.ccsdk.apps.cadi.Taf.LifeForm;
+import org.onap.ccsdk.apps.cadi.principal.TaggedPrincipal;
+
+public class JU_EpiTaf {
+
+    @Test(expected = CadiException.class)
+    @SuppressWarnings("unused")
+    public void constructorTest() throws CadiException {
+        EpiTaf et = new EpiTaf();
+    }
+
+    @Test
+    public void validateTryAnotherTest() throws CadiException {
+        EpiTaf et = new EpiTaf(new TryAnotherTaf());
+        TafResp output = et.validate(LifeForm.CBLF);
+        assertThat(output.isAuthenticated(), is(RESP.NO_FURTHER_PROCESSING));
+    }
+
+    @Test
+    public void validateTryAuthenticatingTest() throws CadiException {
+        EpiTaf et = new EpiTaf(new TryAuthenticatingTaf(), new TryAuthenticatingTaf());
+        TafResp output = et.validate(LifeForm.CBLF);
+        assertThat(output.isAuthenticated(), is(RESP.TRY_AUTHENTICATING));
+        output = et.validate(LifeForm.CBLF);
+        assertThat(output.isAuthenticated(), is(RESP.TRY_AUTHENTICATING));
+    }
+
+    @Test
+    public void validateDefaultCaseTest() throws CadiException {
+        EpiTaf et = new EpiTaf(new NullTaf());
+        TafResp output = et.validate(LifeForm.CBLF);
+        assertThat(output.isAuthenticated(), is(RESP.NO_FURTHER_PROCESSING));
+    }
+
+    class TryAnotherTafResp implements TafResp {
+        @Override public boolean isValid() { return false; }
+        @Override public String desc() { return null; }
+        @Override public RESP isAuthenticated() { return RESP.TRY_ANOTHER_TAF; }
+        @Override public RESP authenticate() throws IOException { return null; }
+        @Override public TaggedPrincipal getPrincipal() { return null; }
+        @Override public String getTarget() {return "unknown";}
+        @Override public Access getAccess() { return null; }
+        @Override public boolean isFailedAttempt() { return false; }
+        @Override public float timing() { return 0; }
+        @Override public void timing(long start) {}
+        @Override public String taf() {return "JUnit";}
+    }
+
+    class TryAnotherTaf implements Taf {
+        @Override public TafResp validate(LifeForm reading, String ... info) { return new TryAnotherTafResp(); }
+    }
+
+    class TryAuthenticatingResp implements TafResp {
+        @Override public boolean isValid() { return false; }
+        @Override public String desc() { return null; }
+        @Override public RESP isAuthenticated() { return RESP.TRY_AUTHENTICATING; }
+        @Override public RESP authenticate() throws IOException { return null; }
+        @Override public TaggedPrincipal getPrincipal() { return null; }
+        @Override public String getTarget() {return "unknown";}
+        @Override public Access getAccess() { return null; }
+        @Override public boolean isFailedAttempt() { return false; }
+        @Override public float timing() { return 0; }
+        @Override public void timing(long start) {}
+        @Override public String taf() {return "JUnit";}
+    }
+
+    class TryAuthenticatingTaf implements Taf {
+        @Override public TafResp validate(LifeForm reading, String ... info) { return new TryAuthenticatingResp(); }
+    }
+
+    class EpiTafStub extends EpiTaf {
+        public EpiTafStub() throws CadiException { }
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/test/JU_HttpEpiTaf.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/test/JU_HttpEpiTaf.java
new file mode 100644 (file)
index 0000000..4819540
--- /dev/null
@@ -0,0 +1,145 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.ccsdk.apps.cadi.Access.Level;
+import org.onap.ccsdk.apps.cadi.CachedPrincipal.Resp;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.Locator;
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.Taf.LifeForm;
+import org.onap.ccsdk.apps.cadi.TrustChecker;
+import org.onap.ccsdk.apps.cadi.taf.HttpEpiTaf;
+import org.onap.ccsdk.apps.cadi.taf.HttpTaf;
+import org.onap.ccsdk.apps.cadi.taf.NullTaf;
+import org.onap.ccsdk.apps.cadi.taf.Redirectable;
+import org.onap.ccsdk.apps.cadi.taf.TafResp;
+import org.onap.ccsdk.apps.cadi.taf.TafResp.RESP;
+
+public class JU_HttpEpiTaf {
+
+    private PropAccess access;
+
+    @Mock private Locator<URI> locMock;
+    @Mock private TrustChecker trustCheckerMock;
+    @Mock private HttpServletRequest reqMock;
+    @Mock private HttpServletResponse respMock;
+    @Mock private HttpTaf tafMock;
+    @Mock private TafResp trespMock;
+    @Mock private Redirectable redirMock;
+
+    @Before
+    public void setup() throws URISyntaxException {
+        MockitoAnnotations.initMocks(this);
+
+        access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]);
+    }
+
+    @Test
+    public void test() throws Exception {
+        HttpEpiTaf taf;
+        try {
+            taf = new HttpEpiTaf(access, locMock, trustCheckerMock);
+            fail("Should've thrown an exception");
+        } catch (CadiException e) {
+            assertThat(e.getMessage(), is("Need at least one HttpTaf implementation in constructor"));
+        }
+
+        taf = new HttpEpiTaf(access, locMock, trustCheckerMock, new NullTaf());
+        taf.validate(LifeForm.CBLF, reqMock, respMock);
+
+        // Coverage of tricorderScan
+        taf.validate(LifeForm.LFN, reqMock, respMock);
+        when(reqMock.getHeader("User-Agent")).thenReturn("Non-mozilla-header");
+        taf.validate(LifeForm.LFN, reqMock, respMock);
+        when(reqMock.getHeader("User-Agent")).thenReturn("Mozilla-header");
+        taf.validate(LifeForm.LFN, reqMock, respMock);
+
+        access.setLogLevel(Level.DEBUG);
+        taf.validate(LifeForm.CBLF, reqMock, respMock);
+
+        when(tafMock.validate(LifeForm.CBLF, reqMock, respMock)).thenReturn(trespMock);
+        when(trespMock.isAuthenticated()).thenReturn(RESP.TRY_ANOTHER_TAF);
+        taf = new HttpEpiTaf(access, locMock, trustCheckerMock, tafMock);
+        taf.validate(LifeForm.CBLF, reqMock, respMock);
+
+        when(trespMock.isAuthenticated()).thenReturn(RESP.IS_AUTHENTICATED);
+        taf.validate(LifeForm.CBLF, reqMock, respMock);
+
+        when(trespMock.isAuthenticated()).thenReturn(RESP.TRY_AUTHENTICATING);
+        taf.validate(LifeForm.CBLF, reqMock, respMock);
+
+        taf = new HttpEpiTaf(access, locMock, trustCheckerMock, tafMock, tafMock);
+        taf.validate(LifeForm.CBLF, reqMock, respMock);
+
+        when(tafMock.validate(LifeForm.CBLF, reqMock, respMock)).thenReturn(redirMock);
+        when(redirMock.isAuthenticated()).thenReturn(RESP.TRY_AUTHENTICATING);
+        taf.validate(LifeForm.CBLF, reqMock, respMock);
+
+        taf = new HttpEpiTaf(access, locMock, trustCheckerMock, tafMock, tafMock);
+        taf.validate(LifeForm.CBLF, reqMock, respMock);
+
+        taf = new HttpEpiTaf(access, locMock, trustCheckerMock, tafMock);
+        taf.validate(LifeForm.CBLF, reqMock, respMock);
+
+        taf = new HttpEpiTaf(access, locMock, null, tafMock);
+        when(redirMock.isAuthenticated()).thenReturn(RESP.IS_AUTHENTICATED);
+        try {
+            taf.validate(LifeForm.CBLF, reqMock, respMock);
+            fail("Should've thrown an exception");
+        } catch (Exception e) {
+        }
+
+        assertThat(taf.revalidate(null), is(false));
+        assertThat(taf.revalidate(null), is(false));
+
+        when(tafMock.revalidate(null, null)).thenReturn(Resp.NOT_MINE);
+        assertThat(taf.revalidate(null, null), is(Resp.NOT_MINE));
+        when(tafMock.revalidate(null, null)).thenReturn(Resp.REVALIDATED);
+        assertThat(taf.revalidate(null, null), is(Resp.REVALIDATED));
+
+        when(tafMock.revalidate(null, null)).thenReturn(Resp.NOT_MINE).thenReturn(Resp.NOT_MINE).thenReturn(Resp.REVALIDATED);
+        taf = new HttpEpiTaf(access, locMock, trustCheckerMock, tafMock, tafMock, tafMock);
+        assertThat(taf.revalidate(null, null), is(Resp.REVALIDATED));
+
+        taf.toString();
+
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/test/JU_LoginPageTafResp.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/test/JU_LoginPageTafResp.java
new file mode 100644 (file)
index 0000000..9d7a9d4
--- /dev/null
@@ -0,0 +1,101 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.taf.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.Locator;
+import org.onap.ccsdk.apps.cadi.Locator.Item;
+import org.onap.ccsdk.apps.cadi.LocatorException;
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.taf.LoginPageTafResp;
+import org.onap.ccsdk.apps.cadi.taf.Redirectable;
+import org.onap.ccsdk.apps.cadi.taf.TafResp;
+import org.onap.ccsdk.apps.cadi.taf.TafResp.RESP;
+
+public class JU_LoginPageTafResp {
+
+    private static final String uriString = "example.com";
+
+    private URI uri;
+    private Access access;
+    private List<Redirectable> redirectables;
+
+    @Mock private HttpServletResponse respMock;
+    @Mock private Locator<URI> locatorMock;
+    @Mock private Redirectable redirMock;
+
+    @Before
+    public void setup() throws URISyntaxException {
+        MockitoAnnotations.initMocks(this);
+
+        access = new PropAccess(new PrintStream(new ByteArrayOutputStream()), new String[0]);
+
+        redirectables = new ArrayList<>();
+        uri = new URI(uriString);
+    }
+
+    @Test
+    public void test() throws LocatorException, IOException {
+        TafResp resp;
+        resp = LoginPageTafResp.create(access, null, respMock, redirectables);
+        assertThat(resp.desc(), is("All Authentication denied"));
+
+        redirectables.add(redirMock);
+        redirectables.add(redirMock);
+        resp = LoginPageTafResp.create(access, null, respMock, redirectables);
+        assertThat((Redirectable)resp, is(redirMock));
+
+        resp = LoginPageTafResp.create(access, locatorMock, respMock, redirectables);
+        assertThat(resp.desc(), is("All Authentication denied"));
+
+        when(locatorMock.get((Item)any())).thenReturn(uri);
+        resp = LoginPageTafResp.create(access, locatorMock, respMock, redirectables);
+        assertThat(resp.desc(), is("Multiple Possible HTTP Logins available.  Redirecting to Login Choice Page"));
+        assertThat(resp.authenticate(), is(RESP.HTTP_REDIRECT_INVOKED));
+        assertThat(resp.isAuthenticated(), is(RESP.TRY_AUTHENTICATING));
+
+        redirectables = new ArrayList<>();
+        resp = LoginPageTafResp.create(access, locatorMock, respMock, redirectables);
+        assertThat(resp.desc(), is("All Authentication denied"));
+
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/test/JU_NullTaf.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/test/JU_NullTaf.java
new file mode 100644 (file)
index 0000000..cb3ac3f
--- /dev/null
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.taf.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import java.io.IOException;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.CachedPrincipal.Resp;
+import org.onap.ccsdk.apps.cadi.taf.TafResp;
+import org.onap.ccsdk.apps.cadi.taf.TafResp.RESP;
+
+import org.onap.ccsdk.apps.cadi.taf.NullTaf;
+
+public class JU_NullTaf {
+
+    @Test
+    public void test() throws IOException {
+        NullTaf nt = new NullTaf();
+        TafResp singleton1 = nt.validate(null);
+        TafResp singleton2 = nt.validate(null, null, null);
+        Resp singleton3 = nt.revalidate(null, null);
+
+        assertThat(singleton1, is(singleton2));
+
+        assertFalse(singleton1.isValid());
+
+        assertThat(singleton1.isAuthenticated(), is(RESP.NO_FURTHER_PROCESSING));
+
+        assertThat(singleton1.desc(), is("All Authentication denied"));
+
+        assertThat(singleton1.authenticate(), is(RESP.NO_FURTHER_PROCESSING));
+
+        assertThat(singleton1.getPrincipal(), is(nullValue()));
+
+        assertThat(singleton1.getAccess(), is(Access.NULL));
+
+        assertTrue(singleton1.isFailedAttempt());
+
+        assertThat(singleton3, is(Resp.NOT_MINE));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/test/JU_PuntTafResp.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/test/JU_PuntTafResp.java
new file mode 100644 (file)
index 0000000..3db3e86
--- /dev/null
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.taf.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import java.io.IOException;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.taf.TafResp.RESP;
+
+import org.onap.ccsdk.apps.cadi.taf.PuntTafResp;
+
+
+public class JU_PuntTafResp {
+
+    @Test
+    public void test() throws IOException {
+        String name = "name";
+        String explanation = "example explanation";
+
+        PuntTafResp punt = new PuntTafResp(name, explanation);
+
+        assertFalse(punt.isValid());
+        assertThat(punt.isAuthenticated(), is(RESP.TRY_ANOTHER_TAF));
+        assertThat(punt.desc(), is("Not processing this transaction: " + explanation));
+        assertThat(punt.taf(), is(name));
+        assertThat(punt.authenticate(), is(RESP.TRY_ANOTHER_TAF));
+        assertThat(punt.getPrincipal(), is(nullValue()));
+        assertThat(punt.getAccess(), is(Access.NULL));
+        assertFalse(punt.isFailedAttempt());
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/test/JU_TrustNotTafResp.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/test/JU_TrustNotTafResp.java
new file mode 100644 (file)
index 0000000..8c9ebfc
--- /dev/null
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.taf.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import static org.mockito.Mockito.*;
+import org.junit.*;
+import org.mockito.*;
+
+import java.io.IOException;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.taf.TafResp;
+import org.onap.ccsdk.apps.cadi.taf.TafResp.RESP;
+import org.onap.ccsdk.apps.cadi.taf.TrustNotTafResp;
+import org.onap.ccsdk.apps.cadi.principal.TaggedPrincipal;
+
+public class JU_TrustNotTafResp {
+
+    @Mock
+    TafResp delegateMock;
+
+    @Mock
+    TaggedPrincipal principalMock;
+
+    @Mock
+    Access accessMock;
+
+    private final String description = "Example Description";
+
+    @Before
+    public void setup() throws IOException {
+        MockitoAnnotations.initMocks(this);
+
+        when(delegateMock.getPrincipal()).thenReturn(principalMock);
+        when(delegateMock.getAccess()).thenReturn(accessMock);
+    }
+
+    @Test
+    public void test() throws IOException {
+        TrustNotTafResp ttr = new TrustNotTafResp(delegateMock, description);
+        assertThat(ttr.isValid(), is(false));
+        assertThat(ttr.desc(), is(description));
+        assertThat(ttr.authenticate(), is(RESP.NO_FURTHER_PROCESSING));
+        assertThat(ttr.isAuthenticated(), is(RESP.NO_FURTHER_PROCESSING));
+        assertThat(ttr.getPrincipal(), is(principalMock));
+        assertThat(ttr.getAccess(), is(accessMock));
+        assertThat(ttr.isFailedAttempt(), is(true));
+        assertThat(ttr.toString(), is(description));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/test/JU_TrustTafResp.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/taf/test/JU_TrustTafResp.java
new file mode 100644 (file)
index 0000000..a4208ab
--- /dev/null
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.taf.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import static org.mockito.Mockito.*;
+import org.junit.*;
+import org.mockito.*;
+
+import java.io.IOException;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.taf.TafResp;
+import org.onap.ccsdk.apps.cadi.taf.TafResp.RESP;
+import org.onap.ccsdk.apps.cadi.taf.TrustTafResp;
+import org.onap.ccsdk.apps.cadi.principal.TaggedPrincipal;
+
+public class JU_TrustTafResp {
+
+    @Mock
+    TafResp delegateMock;
+
+    @Mock
+    TaggedPrincipal principalMock;
+
+    @Mock
+    Access accessMock;
+
+    private final String description = "Example Description";
+    private final String anotherDescription = "Another Description";
+    private final String name = "name";
+
+    private final RESP resp = RESP.IS_AUTHENTICATED;
+
+    @Before
+    public void setup() throws IOException {
+        MockitoAnnotations.initMocks(this);
+
+        when(delegateMock.desc()).thenReturn(anotherDescription);
+        when(delegateMock.isValid()).thenReturn(true);
+        when(delegateMock.isAuthenticated()).thenReturn(resp);
+        when(delegateMock.authenticate()).thenReturn(resp);
+        when(delegateMock.getAccess()).thenReturn(accessMock);
+        when(delegateMock.isFailedAttempt()).thenReturn(true);
+
+        when(principalMock.getName()).thenReturn(name);
+    }
+
+    @Test
+    public void test() throws IOException {
+        TrustTafResp ttr = new TrustTafResp(delegateMock, principalMock, description);
+        assertThat(ttr.isValid(), is(true));
+        assertThat(ttr.desc(), is(description + ' ' + anotherDescription));
+        assertThat(ttr.authenticate(), is(resp));
+        assertThat(ttr.isAuthenticated(), is(resp));
+        assertThat(ttr.getPrincipal(), is(principalMock));
+        assertThat(ttr.getAccess(), is(accessMock));
+        assertThat(ttr.isFailedAttempt(), is(true));
+        assertThat(ttr.toString(), is(name + " by trust of " + description + ' ' + anotherDescription));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_AES.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_AES.java
new file mode 100644 (file)
index 0000000..2851af3
--- /dev/null
@@ -0,0 +1,195 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.test;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+import org.junit.*;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.lang.reflect.Field;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.SecretKey;
+
+import org.onap.ccsdk.apps.cadi.AES;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.Symm;
+
+public class JU_AES {
+    private AES aes;
+    private ByteArrayInputStream baisEncrypt;
+    private ByteArrayInputStream baisDecrypt;
+    private ByteArrayOutputStream baosEncrypt;
+    private ByteArrayOutputStream baosDecrypt;
+
+    private ByteArrayOutputStream errStream;
+
+    @Before
+    public void setup() throws Exception {
+         byte[] keyBytes = new byte[AES.AES_KEY_SIZE/8];
+         char[] codeset = Symm.base64.codeset;
+         int offset = (Math.abs(codeset[0]) + 47) % (codeset.length - keyBytes.length);
+         for (int i = 0; i < keyBytes.length; ++i) {
+             keyBytes[i] = (byte)codeset[i+offset];
+         }
+         aes = new AES(keyBytes, 0, keyBytes.length);
+
+        errStream = new ByteArrayOutputStream();
+        System.setErr(new PrintStream(errStream));
+    }
+
+    @After
+    public void tearDown() {
+        System.setErr(System.err);
+    }
+
+    @Test
+    public void newKeyTest() throws Exception {
+        SecretKey secretKey = AES.newKey();
+        assertThat(secretKey.getAlgorithm(), is(AES.class.getSimpleName()));
+    }
+
+    @Test
+    public void encryptDecrpytFromBytes() throws Exception {
+        String orig = "I'm a password, really";
+        byte[] encrypted = aes.encrypt(orig.getBytes());
+        byte[] decrypted = aes.decrypt(encrypted);
+        assertThat(new String(decrypted), is(orig));
+
+        Field aeskeySpec_field = AES.class.getDeclaredField("aeskeySpec");
+        aeskeySpec_field.setAccessible(true);
+        aeskeySpec_field.set(aes, null);
+
+        try {
+            aes.encrypt(orig.getBytes());
+            fail("Should have thrown an exception");
+        } catch (CadiException e) {
+        }
+        try {
+            aes.decrypt(encrypted);
+            fail("Should have thrown an exception");
+        } catch (CadiException e) {
+        }
+    }
+
+    @Test
+    public void saveToFileTest() throws Exception {
+        String filePath = "src/test/resources/output_key";
+        File keyfile = new File(filePath);
+        aes.save(keyfile);
+        assertTrue(Files.isReadable(Paths.get(filePath)));
+        assertFalse(Files.isWritable(Paths.get(filePath)));
+        assertFalse(Files.isExecutable(Paths.get(filePath)));
+        keyfile.delete();
+    }
+
+    @Test
+    public void encryptDecryptFromInputStream() throws Exception {
+        String orig = "I'm a password, really";
+        byte[] b64encrypted;
+        String output;
+
+        CipherInputStream cisEncrypt;
+        CipherInputStream cisDecrypt;
+
+        // Test CipherInputStream
+        baisEncrypt = new ByteArrayInputStream(orig.getBytes());
+        cisEncrypt = aes.inputStream(baisEncrypt, true);
+        baosEncrypt = new ByteArrayOutputStream();
+        transferFromInputStreamToOutputStream(cisEncrypt, baosEncrypt);
+        cisEncrypt.close();
+
+        b64encrypted = baosEncrypt.toByteArray();
+
+        baisDecrypt = new ByteArrayInputStream(b64encrypted);
+        cisDecrypt = aes.inputStream(baisDecrypt, false);
+        baosDecrypt = new ByteArrayOutputStream();
+        transferFromInputStreamToOutputStream(cisDecrypt, baosDecrypt);
+        cisDecrypt.close();
+
+        output = new String(baosDecrypt.toByteArray());
+        assertThat(output, is(orig));
+
+        Field aeskeySpec_field = AES.class.getDeclaredField("aeskeySpec");
+        aeskeySpec_field.setAccessible(true);
+        aeskeySpec_field.set(aes, null);
+
+        assertNull(aes.inputStream(baisEncrypt, true));
+        assertThat(errStream.toString(), is("Error creating Aes CipherInputStream\n"));
+    }
+
+    @Test
+    public void encryptDecryptFromOutputStream() throws Exception {
+        String orig = "I'm a password, really";
+        byte[] b64encrypted;
+        String output;
+
+        CipherOutputStream cosEncrypt;
+        CipherOutputStream cosDecrypt;
+
+        // Test CipherOutputStream
+        baisEncrypt = new ByteArrayInputStream(orig.getBytes());
+        baosEncrypt = new ByteArrayOutputStream();
+        cosEncrypt = aes.outputStream(baosEncrypt, true);
+        transferFromInputStreamToOutputStream(baisEncrypt, cosEncrypt);
+        cosEncrypt.close();
+
+        b64encrypted = baosEncrypt.toByteArray();
+
+        baosDecrypt = new ByteArrayOutputStream();
+        cosDecrypt = aes.outputStream(baosDecrypt, false);
+        baisDecrypt = new ByteArrayInputStream(b64encrypted);
+        transferFromInputStreamToOutputStream(baisDecrypt, cosDecrypt);
+        cosDecrypt.close();
+
+        output = new String(baosDecrypt.toByteArray());
+        assertThat(output, is(orig));
+
+        Field aeskeySpec_field = AES.class.getDeclaredField("aeskeySpec");
+        aeskeySpec_field.setAccessible(true);
+        aeskeySpec_field.set(aes, null);
+
+        assertNull(aes.outputStream(baosEncrypt, true));
+        assertThat(errStream.toString(), is("Error creating Aes CipherOutputStream\n"));
+    }
+
+    public void transferFromInputStreamToOutputStream(InputStream is, OutputStream os) throws IOException {
+        byte[] buffer = new byte[200];
+        int len;
+        while ((len = is.read(buffer)) != -1) {
+            os.write(buffer, 0, len);
+        }
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_AbsUserCache.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_AbsUserCache.java
new file mode 100644 (file)
index 0000000..f3afee8
--- /dev/null
@@ -0,0 +1,351 @@
+/*******************************************************************************
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.reflect.Field;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.ccsdk.apps.cadi.AbsUserCache;
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.CachedPrincipal.Resp;
+import org.onap.ccsdk.apps.cadi.CachingLur;
+import org.onap.ccsdk.apps.cadi.GetCred;
+import org.onap.ccsdk.apps.cadi.Permission;
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.User;
+import org.onap.ccsdk.apps.cadi.lur.LocalPermission;
+import org.onap.ccsdk.apps.cadi.principal.CachedBasicPrincipal;
+
+public class JU_AbsUserCache {
+
+    @Mock private CachingLur<Permission> cl;
+    @Mock private Principal principal;
+    @Mock private CachedBasicPrincipal cbp;
+    @Mock private LocalPermission permission1;
+    @Mock private LocalPermission permission2;
+
+    private Access access;
+
+    private ByteArrayOutputStream outStream;
+
+    private String name1 = "name1";
+    private String name2 = "name2";
+    private byte[] password = "password".getBytes();
+
+    private static Field timerField;
+
+    @BeforeClass
+    public static void setupOnce() throws Exception {
+        timerField = AbsUserCache.class.getDeclaredField("timer");
+        timerField.setAccessible(true);
+    }
+
+    @Before
+    public void setup() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        outStream = new ByteArrayOutputStream();
+        System.setOut(new PrintStream(outStream));
+
+        // This must happen after changing System.out
+        access = new PropAccess();
+
+        when(permission1.getKey()).thenReturn("NewKey1");
+        when(permission2.getKey()).thenReturn("NewKey2");
+
+        timerField.set(null, null);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        System.setOut(System.out);
+        timerField.set(null, null);
+    }
+
+    @SuppressWarnings("unused")
+    @Test
+    public void constructorTest() {
+        int cleanInterval = 65000;
+        int maxInterval = 70000;
+
+        AbsUserCacheStub<Permission> aucs1 = new AbsUserCacheStub<Permission>(access, cleanInterval, maxInterval, Integer.MAX_VALUE);
+        String output = outStream.toString().split(" ", 2)[1];
+
+        outStream.reset();
+        AbsUserCacheStub<Permission> aucs2 = new AbsUserCacheStub<Permission>(access, cleanInterval, maxInterval, Integer.MAX_VALUE);
+        output = outStream.toString().split(" ", 2)[1];
+
+        AbsUserCacheStub<Permission> aucs3 = new AbsUserCacheStub<Permission>(access, 0, 0, Integer.MAX_VALUE);
+        AbsUserCacheStub<Permission> aucs4 = new AbsUserCacheStub<Permission>(aucs1);
+
+        // For coverage
+        AbsUserCacheCLStub<Permission> auccls1 = new AbsUserCacheCLStub<Permission>(aucs1);
+        aucs1.setLur(cl);
+        auccls1 = new AbsUserCacheCLStub<Permission>(aucs1);
+        AbsUserCacheCLStub<Permission> auccls2 = new AbsUserCacheCLStub<Permission>(aucs3);
+    }
+
+    @Test
+    public void setLurTest() {
+        AbsUserCacheStub<Permission> aucs1 = new AbsUserCacheStub<Permission>(access, 65000, 70000, Integer.MAX_VALUE);
+        AbsUserCacheStub<Permission> aucs2 = new AbsUserCacheStub<Permission>(access, 0, 0, Integer.MAX_VALUE);
+        aucs1.setLur(cl);
+        aucs2.setLur(cl);
+    }
+
+    @Test
+    public void addUserGetUserTest() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        AbsUserCacheStub<Permission> aucs = new AbsUserCacheStub<Permission>(access, 0, 0, Integer.MAX_VALUE);
+        User<Permission> user;
+
+        // Test adding a user with a principal (non-GetCred). user does not have a cred
+        // Then test getting that user
+        when(principal.getName()).thenReturn(name1);
+        user = new User<Permission>(principal, 0);
+        aucs.addUser(user);
+        assertThat(aucs.getUser(principal), is(user));
+
+        // Test adding a user with a principal (GetCred). user does not have a cred
+        // Then test getting that user
+        GetCredStub gc = new GetCredStub();
+        user = new User<Permission>(gc, 0);
+        aucs.addUser(user);
+        assertThat(aucs.getUser(gc), is(user));
+
+        // Test adding a user with no principal
+        // Then test getting that user via his name and cred
+        user = new User<Permission>(name2, password);
+        aucs.addUser(user);
+        assertThat(aucs.getUser(name2, password), is(user));
+
+        // Test getting a user by a CachedBasicPrincipal
+        when(cbp.getName()).thenReturn(name2);
+        when(cbp.getCred()).thenReturn(password);
+        assertThat(aucs.getUser(cbp), is(user));
+
+        // Force the user to expire, then test that he is no longer in the cache
+        Field permExpiresField = User.class.getDeclaredField("permExpires");
+        permExpiresField.setAccessible(true);
+        permExpiresField.set(user, 0);
+        assertThat(aucs.getUser(name2, password), is(nullValue()));
+
+        // Test adding a user with a custom key
+        // Then test gettin that user
+        user = new User<Permission>(principal, 0);
+        String key = principal.getName() + "NoCred";
+        aucs.addUser(key, user);
+        assertThat(aucs.getUser(principal), is(user));
+
+        // Test that getUser returns null for principals that don't match any users
+        when(principal.getName()).thenReturn("not in the cache");
+        assertThat(aucs.getUser(principal), is(nullValue()));
+
+        // That that getUser returns null for name/creds that are not in the cache
+        assertThat(aucs.getUser("not a real user", "not in the cache".getBytes()), is(nullValue()));
+    }
+
+    @Test
+    public void removeTest() {
+        AbsUserCacheStub<Permission> aucs = new AbsUserCacheStub<Permission>(access, 0, 0, Integer.MAX_VALUE);
+        User<Permission> user;
+
+        when(principal.getName()).thenReturn(name1);
+        user = new User<Permission>(principal);
+        // Add a user with a principal
+        aucs.addUser(user);
+        // Check that the user is in the cache
+        assertThat(aucs.getUser(principal), is(user));
+        // Remove the user
+        when(principal.getName()).thenReturn(name1 + "NoCred");
+        aucs.remove(user);
+        // Check that the user is no longer in the cache
+        when(principal.getName()).thenReturn(name1);
+        assertThat(aucs.getUser(principal), is(nullValue()));
+
+        // Add the user again
+        aucs.addUser(user);
+        // Check that the user is in the cache
+        assertThat(aucs.getUser(principal), is(user));
+        // Remove the user by name
+        aucs.remove(name1 + "NoCred");
+        // Check that the user is no longer in the cache
+        assertThat(aucs.getUser(principal), is(nullValue()));
+
+        // Coverage test - attempt to remove a user that is not in the cache
+        aucs.remove(name1 + "NoCred");
+        assertThat(aucs.getUser(principal), is(nullValue()));
+    }
+
+    @Test
+    public void clearAllTest() {
+        AbsUserCacheStub<Permission> aucs = new AbsUserCacheStub<Permission>(access, 0, 0, Integer.MAX_VALUE);
+        User<Permission> user1;
+        User<Permission> user2;
+
+        // Add some users to the cache
+        when(principal.getName()).thenReturn(name1);
+        user1 = new User<Permission>(principal);
+        when(principal.getName()).thenReturn(name2);
+        user2 = new User<Permission>(principal);
+        aucs.addUser(user1);
+        aucs.addUser(user2);
+
+        // Check that the users are in the cache
+        when(principal.getName()).thenReturn(name1);
+        assertThat(aucs.getUser(principal), is(user1));
+        when(principal.getName()).thenReturn(name2);
+        assertThat(aucs.getUser(principal), is(user2));
+
+        // Clear the cache
+        aucs.clearAll();
+
+        // Check that the users are no longer in the cache
+        when(principal.getName()).thenReturn(name1);
+        assertThat(aucs.getUser(principal), is(nullValue()));
+        when(principal.getName()).thenReturn(name2);
+        assertThat(aucs.getUser(principal), is(nullValue()));
+    }
+
+    @Test
+    public void dumpInfoTest() {
+        AbsUserCacheStub<Permission> aucs = new AbsUserCacheStub<Permission>(access, 0, 0, Integer.MAX_VALUE);
+        User<Permission> user1;
+        User<Permission> user2;
+
+        Principal principal1 = mock(Principal.class);
+        Principal principal2 = mock(Principal.class);
+        when(principal1.getName()).thenReturn(name1);
+        when(principal2.getName()).thenReturn(name2);
+
+        // Add some users with permissions to the cache
+        user1 = new User<Permission>(principal1);
+        user1.add(permission1);
+        user1.add(permission2);
+        user2 = new User<Permission>(principal2);
+        user2.add(permission1);
+        user2.add(permission2);
+        aucs.addUser(user1);
+        aucs.addUser(user2);
+
+        // Dump the info
+        List<AbsUserCache<Permission>.DumpInfo> dumpInfo = aucs.dumpInfo();
+        assertThat(dumpInfo.size(), is(2));
+
+        // Utility lists
+        List<String> names = new ArrayList<>();
+        names.add(name1);
+        names.add(name2);
+        List<String> permissions = new ArrayList<>();
+        permissions.add("NewKey1");
+        permissions.add("NewKey2");
+
+        // We need to use "contains" because the dumpInfo was created from a list, so we don't know it's order
+        for (AbsUserCache<Permission>.DumpInfo di : dumpInfo) {
+            assertTrue(names.contains(di.user));
+            for (String perm : di.perms) {
+                assertTrue(permissions.contains(perm));
+            }
+        }
+    }
+
+    @Test
+    public void handlesExclusivelyTest() {
+        AbsUserCacheStub<Permission> aucs = new AbsUserCacheStub<Permission>(access, 0, 0, Integer.MAX_VALUE);
+        assertFalse(aucs.handlesExclusively(permission1));
+        assertFalse(aucs.handlesExclusively(permission2));
+    }
+
+    @Test
+    public void destroyTest() {
+        AbsUserCacheStub<Permission> aucs = new AbsUserCacheStub<Permission>(access, 0, 0, Integer.MAX_VALUE);
+        aucs.destroy();
+        aucs = new AbsUserCacheStub<Permission>(access, 1, 1, Integer.MAX_VALUE);
+        aucs.destroy();
+    }
+
+    @Test
+    public void missTest() throws IOException {
+        AbsUserCacheStub<Permission> aucs = new AbsUserCacheStub<Permission>(access, 0, 0, Integer.MAX_VALUE);
+        // Add the Miss to the missmap
+        assertTrue(aucs.addMiss("key", password));  // This one actually adds it
+        assertTrue(aucs.addMiss("key", password));  // this one doesn't really do anything
+        assertTrue(aucs.addMiss("key", password));  // neither does this one
+        assertFalse(aucs.addMiss("key", password)); // By this time, the missMap is tired of this nonsense, and retaliates
+        assertFalse(aucs.addMiss("key", password)); // Oh yea. He's angry
+
+        // Can't really test this due to visibility
+        aucs.missed("key", password);
+
+        // Coverage
+        AbsUserCacheStub<Permission> aucs1 = new AbsUserCacheStub<Permission>(access, 1, 1, Integer.MAX_VALUE);
+        aucs1.addMiss("key", password);
+    }
+
+    class AbsUserCacheStub<PERM extends Permission> extends AbsUserCache<PERM> {
+        public AbsUserCacheStub(Access access, long cleanInterval, int highCount, int usageCount) { super(access, cleanInterval, highCount, usageCount); }
+        public AbsUserCacheStub(AbsUserCache<PERM> cache) { super(cache); }
+        @Override public void setLur(CachingLur<PERM> lur) { super.setLur(lur); }
+        @Override public void addUser(User<PERM> user) { super.addUser(user); }
+        @Override public void addUser(String key, User<PERM> user) { super.addUser(key, user); }
+        @Override public User<PERM> getUser(Principal p) { return super.getUser(p); }
+        @Override public User<PERM> getUser(CachedBasicPrincipal p) { return super.getUser(p); }
+        @Override public User<PERM> getUser(String user, byte[] cred) { return super.getUser(user, cred); }
+        @Override public void remove(User<PERM> user) { super.remove(user); }
+        @Override public boolean addMiss(String key, byte[] bs) { return super.addMiss(key, bs); }
+        @Override public Miss missed(String key, byte[] bs) throws IOException { return super.missed(key, bs); }
+    }
+
+    class AbsUserCacheCLStub<PERM extends Permission> extends AbsUserCache<PERM> implements CachingLur<PERM> {
+        public AbsUserCacheCLStub(AbsUserCache<PERM> cache) { super(cache); }
+        @Override public Permission createPerm(String p) { return null; }
+        @Override public boolean fish(Principal bait, Permission ... pond) { return false; }
+        @Override public void fishAll(Principal bait, List<Permission> permissions) { }
+        @Override public boolean handles(Principal principal) { return false; }
+        @Override public Resp reload(User<PERM> user) { return null; }
+        @Override public void setDebug(String commaDelimIDsOrNull) { }
+    }
+
+    class GetCredStub implements Principal, GetCred {
+        @Override public byte[] getCred() { return password; }
+        @Override public String getName() { return name1; }
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_Access.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_Access.java
new file mode 100644 (file)
index 0000000..a9721d2
--- /dev/null
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import java.io.IOException;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.Access.Level;
+
+public class JU_Access {
+
+    @Test
+    public void levelTests() {
+        assertTrue(Level.DEBUG.inMask(0x1));
+        for (int i = 2; i > 0; i <<= 1) {
+            assertFalse(Level.DEBUG.inMask(i));
+        }
+        assertFalse(Level.DEBUG.inMask(0x80000000));
+
+        assertThat(Level.DEBUG.addToMask(0x2), is(0x3));
+        assertThat(Level.DEBUG.delFromMask(0x1), is(0x0));
+        assertThat(Level.DEBUG.toggle(0x2), is(0x3));
+        assertThat(Level.DEBUG.toggle(0x1), is(0x0));
+        assertThat(Level.DEBUG.maskOf(), is(123153));
+        assertThat(Level.NONE.maskOf(), is(0));
+    }
+
+    @Test
+    public void nullTests() throws IOException {
+        // These are entirely for coverage
+        Access.NULL.log(Level.DEBUG);
+        Access.NULL.printf(Level.DEBUG, "");
+        Access.NULL.log(new Exception());
+        Access.NULL.classLoader();
+        assertThat(Access.NULL.getProperty("", ""), is(nullValue()));
+        Access.NULL.load(System.in);
+        Access.NULL.setLogLevel(Level.DEBUG);
+        assertThat(Access.NULL.decrypt("test", true), is("test"));
+        assertFalse(Access.NULL.willLog(Level.DEBUG));
+        assertThat(Access.NULL.getProperties(), is(not(nullValue())));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_Base64.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_Base64.java
new file mode 100644 (file)
index 0000000..319d98e
--- /dev/null
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.SecureRandom;
+
+import org.junit.Test;
+import org.onap.ccsdk.apps.cadi.Symm;
+import org.onap.ccsdk.apps.cadi.config.Config;
+
+public class JU_Base64 {
+    private static final String encoding = "Man is distinguished, not only by his reason, but by this singular " +
+            "passion from other animals, which is a lust of the mind, that by a " +
+            "perseverance of delight in the continued and indefatigable generation of " +
+            "knowledge, exceeds the short vehemence of any carnal pleasure.";
+
+    private static final String expected =
+            "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz\n" +
+            "IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg\n" +
+            "dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu\n" +
+            "dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo\n" +
+            "ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=";
+
+    @Test
+    public void test() throws Exception {
+        // Test with different Padding
+        assertEncoded("leas",     "bGVhcw==");
+        assertEncoded("leasu",    "bGVhc3U=");
+        assertEncoded("leasur",   "bGVhc3Vy");
+        assertEncoded("leasure",  "bGVhc3VyZQ==");
+        assertEncoded("leasure.", "bGVhc3VyZS4=");
+
+        // Test with line ends
+        assertEncoded(encoding, expected);
+    }
+
+    @Test
+    public void symmetric() throws IOException {
+        String symmetric = new String(Symm.keygen());
+        Symm bsym = Symm.obtain(symmetric);
+        String result = bsym.encode(encoding);
+        assertThat(bsym.decode(result), is(encoding));
+
+        char[] manipulate = symmetric.toCharArray();
+        int spot = new SecureRandom().nextInt(manipulate.length);
+        manipulate[spot]|=0xFF;
+        String newsymmetric = new String(manipulate);
+        assertThat(symmetric, is(not(newsymmetric)));
+        try {
+            bsym = Symm.obtain(newsymmetric);
+            result = bsym.decode(result);
+            assertThat(result, is(encoding));
+        } catch (IOException e) {
+            // this is what we want to see if key wrong
+        }
+    }
+
+    private void assertEncoded(String toEncode, String expected) throws IOException {
+        String result = Symm.base64.encode(toEncode);
+        assertThat(result, is(expected));
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        Symm.base64.decode(new ByteArrayInputStream(result.getBytes()), baos);
+        result = baos.toString(Config.UTF_8);
+        assertThat(result, is(toEncode));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_BufferedCadiWrap.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_BufferedCadiWrap.java
new file mode 100644 (file)
index 0000000..968afb4
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.ccsdk.apps.cadi.test;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class JU_BufferedCadiWrap {
+    @Mock
+    private HttpServletRequest request;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void constructorTest() {
+        // TODO: Ian - This will always fail beacuse the constructor is invalid
+        // BufferedCadiWrap bcw = new BufferedCadiWrap(request);
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_BufferedServletInputStream.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_BufferedServletInputStream.java
new file mode 100644 (file)
index 0000000..a6ac1cf
--- /dev/null
@@ -0,0 +1,321 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.lang.reflect.*;
+import junit.framework.Assert;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.ccsdk.apps.cadi.BufferedServletInputStream;
+
+import static junit.framework.Assert.assertEquals;
+
+public class JU_BufferedServletInputStream {
+    private BufferedServletInputStream bsis;
+    private String expected;
+
+    @Before
+    public void setup() throws FileNotFoundException {
+        expected = new String("This is the expected output");
+        bsis = new BufferedServletInputStream(new ByteArrayInputStream(expected.getBytes()));
+    }
+
+    @After
+    public void tearDown() throws IOException {
+        bsis.close();
+    }
+
+    @Test
+    public void ByteReadNoMarkTest() throws Exception {
+        int c;
+        int i = 0;
+        byte output[] = new byte[100];
+        while ((c = bsis.read()) != -1) {
+            output[i++] = (byte)c;
+        }
+        Assert.assertEquals(new String(output, 0, i), expected);
+    }
+
+    @Test
+    public void ByteReadMarkTest() throws Exception {
+        bsis.mark(0);
+        int c;
+        int i = 0;
+        byte output[] = new byte[100];
+        while ((c = bsis.read()) != -1) {
+            output[i++] = (byte)c;
+        }
+        Assert.assertEquals(new String(output, 0, i), expected);
+    }
+
+    @Test
+    public void ByteReadStateIsStoreTest() throws Exception {
+        Field state_field = BufferedServletInputStream.class.getDeclaredField("state");
+        state_field.setAccessible(true);
+        bsis.mark(0);
+        int c;
+        int i = 0;
+        byte output[] = new byte[100];
+        while ((c = bsis.read()) != -1) {
+            output[i++] = (byte)c;
+        }
+        bsis.reset();
+        Assert.assertEquals(state_field.get(bsis), 2);  // state == READ
+    }
+
+    @Test
+    public void ByteReadStateIsReadTest() throws Exception {
+        bsis.mark(0);  // Initialize the capacitor
+        boolean isReset = false;
+        int c;
+        int i = 0;
+        byte output[] = new byte[100];
+        while ((c = bsis.read()) != -1) {
+            output[i++] = (byte)c;
+            if ((i > 5) && !isReset) {
+                // Close the capacitor and start over. This is done for coverage purposes
+                i = 0;
+                isReset = true;
+                bsis.reset();  // Sets state to READ
+            }
+        }
+        Assert.assertEquals(new String(output, 0, i), expected);
+    }
+
+    @Test
+    public void ByteReadStateIsNoneTest() throws Exception {
+        Field state_field = BufferedServletInputStream.class.getDeclaredField("state");
+        state_field.setAccessible(true);
+        bsis.mark(0);  // Initialize the capacitor
+        int c;
+        c = bsis.read();
+        // Close the capacitor. This is done for coverage purposes
+        bsis.reset();  // Sets state to READ
+        state_field.setInt(bsis, 0);  // state == NONE
+        c = bsis.read();
+        Assert.assertEquals(c, -1);
+    }
+
+    @Test
+    public void ByteArrayReadNoMarkTest() throws Exception {
+        byte output[] = new byte[100];
+        int count = bsis.read(output, 0, expected.length());
+        Assert.assertEquals(new String(output, 0, count), expected);
+        Assert.assertEquals(count, expected.length());
+    }
+
+    @Test
+    public void ByteArrayReadTest() throws Exception {
+        byte[] output = new byte[100];
+        bsis.mark(0);
+        bsis.read(output);
+        Assert.assertEquals(new String(output, 0, expected.length()), expected);
+    }
+
+    @Test
+    public void ByteArrayReadStateIsStoreTest() throws Exception {
+        byte output[] = new byte[100];
+        bsis.mark(0);
+        int count = bsis.read(output, 0, expected.length());
+        Assert.assertEquals(new String(output, 0, count), expected);
+        Assert.assertEquals(count, expected.length());
+
+        count = bsis.read(output, 0, 0);
+        Assert.assertEquals(count, -1);
+    }
+
+    @Test
+    public void ByteArrayReadStateIsReadTest() throws Exception {
+        byte output[] = new byte[200];
+        for (int i = 0; i < 2; ++i) {
+            bsis.mark(0);
+            bsis.read(output, 0, 100);
+            Assert.assertEquals(new String(output, 0, expected.length()), expected);
+
+            bsis.reset();
+            bsis.read(output, 0, output.length);
+            Assert.assertEquals(new String(output, 0, expected.length()), expected);
+            bsis = new BufferedServletInputStream(new ByteArrayInputStream(output));
+            if (i == 0) {
+                output = new byte[200];
+            }
+        }
+
+        Assert.assertEquals(new String(output, 0, expected.length()), expected);
+    }
+
+    @Test
+    public void ByteArrayReadStateIsNoneTest() throws Exception {
+        byte output[] = new byte[100];
+        bsis.mark(0);
+
+        Field state_field = BufferedServletInputStream.class.getDeclaredField("state");
+        state_field.setAccessible(true);
+        state_field.setInt(bsis, 0);  // state == NONE
+
+        int count = bsis.read(output, 0, 100);
+        Assert.assertEquals(count, -1);
+    }
+
+    @Test
+    public void skipTest() throws Exception {
+        byte output[] = new byte[100];
+        bsis.mark(0);
+        bsis.read(output, 0, 10);
+        long count = bsis.skip(200);
+        // skip returns the number left _before_ skipping. that number starts at 256
+        Assert.assertEquals(count, 246);
+
+        count = bsis.skip(200);
+        Assert.assertEquals(count, 17);
+    }
+
+    @Test
+    public void availableTest() throws Exception {
+        int count = bsis.available();
+        Assert.assertEquals(count, 27);
+        bsis.mark(0);
+        count = bsis.available();
+        Assert.assertEquals(count, 27);
+    }
+
+    @Test
+    public void bufferedTest() throws Exception {
+        bsis.mark(0);
+        Assert.assertEquals(bsis.buffered(), 0);
+    }
+
+    @Test
+    public void closeTest() throws Exception {
+        Field capacitor_field = BufferedServletInputStream.class.getDeclaredField("capacitor");
+        capacitor_field.setAccessible(true);
+        bsis.mark(0);
+        Assert.assertNotNull(capacitor_field.get(bsis));
+        bsis.close();
+        Assert.assertNull(capacitor_field.get(bsis));
+    }
+
+    @Test
+    public void markTest() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        Field state_field = BufferedServletInputStream.class.getDeclaredField("state");
+        Field capacitor_field = BufferedServletInputStream.class.getDeclaredField("capacitor");
+        capacitor_field.setAccessible(true);
+        state_field.setAccessible(true);
+
+        // capacitor is null initially
+        Assert.assertNull(capacitor_field.get(bsis));
+
+        state_field.setInt(bsis, 0);  // state == NONE
+        bsis.mark(0);  // the value passed into mark is ignored
+        Assert.assertNotNull(capacitor_field.get(bsis));
+        Assert.assertEquals(state_field.get(bsis), 1);  // state == STORE
+
+        state_field.setInt(bsis, 1);  // state == STORE
+        bsis.mark(0);  // the value passed into mark is ignored
+        Assert.assertEquals(state_field.get(bsis), 1);  // state == STORE
+
+        state_field.setInt(bsis, 2);  // state == READ
+        bsis.mark(0);  // the value passed into mark is ignored
+        Assert.assertEquals(state_field.get(bsis), 1);  // state == STORE
+    }
+
+    @Test
+    public void resetTest() throws Exception {
+        Field state_field = BufferedServletInputStream.class.getDeclaredField("state");
+        state_field.setAccessible(true);
+
+        bsis.mark(0);
+        Assert.assertEquals(state_field.get(bsis), 1);  // state == STORE
+        bsis.reset();
+        Assert.assertEquals(state_field.get(bsis), 2);  // state == READ
+        bsis.reset();
+        Assert.assertEquals(state_field.get(bsis), 2);  // state == READ
+
+        state_field.setInt(bsis, -1);  // state is invalid
+        bsis.reset();  // This call does nothing. It is for coverage alone
+        Assert.assertEquals(state_field.get(bsis), -1);  // state doesn't change
+
+        try {
+            state_field.setInt(bsis, 0);  // state == NONE
+            bsis.reset();
+        } catch (IOException e) {
+            Assert.assertEquals(e.getMessage(), "InputStream has not been marked");
+        }
+    }
+
+    @Test
+    public void markSupportedTest() {
+        Assert.assertTrue(bsis.markSupported());
+    }
+
+    // "Bug" 4/22/2013
+    // Some XML code expects Buffered InputStream can never return 0...  This isn't actually true, but we'll accommodate as far
+    // as we can.
+    // Here, we make sure we set and read the Buffered data, making sure the buffer is empty on the last test...
+    @Test
+    public void issue04_22_2013() throws IOException {
+        String testString = "We want to read in and get out with a Buffered Stream seamlessly.";
+        ByteArrayInputStream bais = new ByteArrayInputStream(testString.getBytes());
+        BufferedServletInputStream bsis = new BufferedServletInputStream(bais);
+        try {
+            bsis.mark(0);
+            byte aa[] = new byte[testString.length()];  // 65 count... important for our test (divisible by 5);
+
+            int read;
+            for (int i=0;i<aa.length;i+=5) {
+                read = bsis.read(aa, i, 5);
+                assertEquals(5,read);
+            }
+            // System.out.println(new String(aa));
+
+            bsis.reset();
+
+            byte bb[] = new byte[aa.length];
+            read = 0;
+            for (int i=0;read>=0;i+=read) {
+                read = bsis.read(bb,i,5);
+                switch(i) {
+                    case 65:
+                        assertEquals(read,-1);
+                        break;
+                    default:
+                        assertEquals(read,5);
+                }
+            }
+            // System.out.println(new String(bb));
+            assertEquals(testString,new String(aa));
+            assertEquals(testString,new String(bb));
+
+        } finally {
+            bsis.close();
+            bais.close();
+        }
+
+    }
+
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_CadiException.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_CadiException.java
new file mode 100644 (file)
index 0000000..8167ce6
--- /dev/null
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.test;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.onap.ccsdk.apps.cadi.CadiException;
+
+import static org.hamcrest.CoreMatchers.is;
+
+public class JU_CadiException {
+    @Test
+    public void testCadiException() {
+        CadiException exception = new CadiException();
+
+        assertNotNull(exception);
+    }
+
+    @Test
+    public void testCadiExceptionString() {
+        CadiException exception = new CadiException("New Exception");
+        assertNotNull(exception);
+        assertThat(exception.getMessage(), is("New Exception"));
+    }
+
+    @Test
+    public void testCadiExceptionThrowable() {
+        CadiException exception = new CadiException(new Throwable("New Exception"));
+        assertNotNull(exception);
+        assertThat(exception.getMessage(), is("java.lang.Throwable: New Exception"));
+    }
+
+    @Test
+    public void testCadiExceptionStringThrowable() {
+        CadiException exception = new CadiException("New Exception",new Throwable("New Exception"));
+        assertNotNull(exception);
+        assertThat(exception.getMessage(), is("New Exception"));
+
+    }
+
+    @Test
+    public void testCadiException1() {
+        CadiException exception = new CadiException();
+
+        assertNotNull(exception);
+    }
+
+    @Test
+    public void testCadiExceptionString1() {
+        CadiException exception = new CadiException("New Exception");
+        assertNotNull(exception);
+        assertThat(exception.getMessage(), is("New Exception"));
+    }
+
+    @Test
+    public void testCadiExceptionThrowable1() {
+        CadiException exception = new CadiException(new Throwable("New Exception"));
+        assertNotNull(exception);
+        assertThat(exception.getMessage(), is("java.lang.Throwable: New Exception"));
+    }
+
+    @Test
+    public void testCadiExceptionStringThrowable1() {
+        CadiException exception = new CadiException("New Exception",new Throwable("New Exception"));
+        assertNotNull(exception);
+        assertThat(exception.getMessage(), is("New Exception"));
+
+    }
+
+    @Test
+    public void testCadiException2() {
+        CadiException exception = new CadiException();
+
+        assertNotNull(exception);
+    }
+
+    @Test
+    public void testCadiExceptionString2() {
+        CadiException exception = new CadiException("New Exception");
+        assertNotNull(exception);
+        assertThat(exception.getMessage(), is("New Exception"));
+    }
+
+    @Test
+    public void testCadiExceptionThrowable2() {
+        CadiException exception = new CadiException(new Throwable("New Exception"));
+        assertNotNull(exception);
+        assertThat(exception.getMessage(), is("java.lang.Throwable: New Exception"));
+    }
+
+    @Test
+    public void testCadiExceptionStringThrowable2() {
+        CadiException exception = new CadiException("New Exception",new Throwable("New Exception"));
+        assertNotNull(exception);
+        assertThat(exception.getMessage(), is("New Exception"));
+
+    }
+
+
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_CadiWrap.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_CadiWrap.java
new file mode 100644 (file)
index 0000000..8adc04e
--- /dev/null
@@ -0,0 +1,162 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.test;
+
+import org.junit.*;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.security.Principal;
+import java.util.List;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.CachingLur;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.CadiWrap;
+import org.onap.ccsdk.apps.cadi.Lur;
+import org.onap.ccsdk.apps.cadi.Permission;
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.User;
+import org.onap.ccsdk.apps.cadi.CachedPrincipal.Resp;
+import org.onap.ccsdk.apps.cadi.filter.MapPermConverter;
+import org.onap.ccsdk.apps.cadi.lur.EpiLur;
+import org.onap.ccsdk.apps.cadi.principal.TaggedPrincipal;
+import org.onap.ccsdk.apps.cadi.taf.TafResp;
+
+public class JU_CadiWrap {
+
+    @Mock
+    private HttpServletRequest request;
+
+    @Mock
+    private TafResp tafResp;
+
+    @Mock
+    private TaggedPrincipal principle;
+
+    @Mock
+    private Lur lur;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        System.setOut(new PrintStream(new ByteArrayOutputStream()));
+    }
+
+    @After
+    public void tearDown() {
+        System.setOut(System.out);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testInstantiate() throws CadiException {
+        Access a = new PropAccess();
+        when(tafResp.getAccess()).thenReturn(a);
+
+        lur.fishAll(isA(Principal.class), (List<Permission>)isA(List.class));
+
+        EpiLur lur1 = new EpiLur(lur);
+
+        CadiWrap wrap = new CadiWrap(request, tafResp, lur1);
+
+        assertNull(wrap.getUserPrincipal());
+        assertNull(wrap.getRemoteUser());
+        assertNull(wrap.getUser());
+        assertEquals(wrap.getPermissions(principle).size(), 0);
+        assertTrue(wrap.access() instanceof PropAccess);
+
+        byte[] arr = {'1','2'};
+        wrap.setCred(arr);
+
+        assertEquals(arr, wrap.getCred());
+
+        wrap.setUser("User1");
+        assertEquals("User1", wrap.getUser());
+
+        wrap.invalidate("1");
+
+        assertFalse(wrap.isUserInRole(null));
+
+        wrap.set(tafResp, lur);
+
+        wrap.invalidate("2");
+
+        assertFalse(wrap.isUserInRole("User1"));
+    }
+
+    @Test
+    public void testInstantiateWithPermConverter() throws CadiException {
+        Access a = new PropAccess();
+        when(tafResp.getAccess()).thenReturn(a);
+        when(tafResp.getPrincipal()).thenReturn(principle);
+
+        // Anonymous object for testing purposes
+        CachingLur<Permission> lur1 = new CachingLur<Permission>() {
+            @Override public Permission createPerm(String p) { return null; }
+            @Override public boolean fish(Principal bait, Permission ... pond) { return true; }
+            @Override public void fishAll(Principal bait, List<Permission> permissions) { }
+            @Override public void destroy() { }
+            @Override public boolean handlesExclusively(Permission ... pond) { return false; }
+            @Override public boolean handles(Principal principal) { return false; }
+            @Override public void remove(String user) { }
+            @Override public Resp reload(User<Permission> user) { return null; }
+            @Override public void setDebug(String commaDelimIDsOrNull) { }
+            @Override public void clear(Principal p, StringBuilder sb) { }
+        };
+
+        MapPermConverter pc = new MapPermConverter();
+
+        CadiWrap wrap = new CadiWrap(request, tafResp, lur1, pc);
+
+        assertNotNull(wrap.getUserPrincipal());
+        assertNull(wrap.getRemoteUser());
+        assertNull(wrap.getUser());
+
+        byte[] arr = {'1','2'};
+        wrap.setCred(arr);
+
+        assertEquals(arr, wrap.getCred());
+
+        wrap.setUser("User1");
+        assertEquals("User1", wrap.getUser());
+
+        wrap.invalidate("1");
+        wrap.setPermConverter(new MapPermConverter());
+
+        assertTrue(wrap.getLur() instanceof CachingLur);
+        assertTrue(wrap.isUserInRole("User1"));
+
+        wrap.set(tafResp, lur);
+        assertFalse(wrap.isUserInRole("Perm1"));
+    }
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_Capacitor.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_Capacitor.java
new file mode 100644 (file)
index 0000000..b0ba781
--- /dev/null
@@ -0,0 +1,156 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.test;
+
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.ccsdk.apps.cadi.Capacitor;
+
+import java.lang.reflect.*;
+
+public class JU_Capacitor {
+    private Capacitor cap;
+    public final static String TEST_DATA =
+            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+            "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" +
+            "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" +
+            "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" +
+            "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" +
+            "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
+
+    @Before
+    public void setup() {
+        cap = new Capacitor();
+    }
+
+    @Test
+    public void singleByteTest() throws Exception {
+        assertEquals(cap.read(), -1);
+        cap.setForRead();
+        Field curr_field = Capacitor.class.getDeclaredField("curr");
+        curr_field.setAccessible(true);
+        Field idx_field = Capacitor.class.getDeclaredField("idx");
+        idx_field.setAccessible(true);
+        assertNull(curr_field.get(cap));
+        assertEquals(idx_field.get(cap), 0);
+
+        for (int iter = 0; iter < 20; ++iter) {
+            for (int i = 0; i < 20; ++i) {
+                cap.put((byte)('a' + i));
+            }
+            cap.setForRead();
+            byte[] array = new byte[20];
+            for (int i = 0; i < 20; ++i) {
+                array[i]=(byte)cap.read();
+            }
+            assertEquals("abcdefghijklmnopqrst", new String(array));
+            assertEquals(-1, cap.read());
+
+            cap.done();
+        }
+
+        for (int i = 0; i < 500; i++) {
+            cap.put((byte)'a');
+        }
+        cap.setForRead();
+        byte[] array = new byte[500];
+        for (int i = 0; i < 500; ++i) {
+            array[i]=(byte)cap.read();
+        }
+        assertEquals((new String(array)).length(), 500);
+        assertEquals(-1, cap.read());
+    }
+
+    @Test
+    public void availableTest() {
+        assertEquals(cap.available(), 0);
+        for (int i = 0; i < 100; ++i) {
+            cap.put((byte)'a');
+        }
+        // The Capacitor can hold 256 bytes. After reading 100 bytes,
+        // it should have 156 available
+        assertEquals(cap.available(), 156);
+    }
+
+    @Test
+    public void byteArrayTest() {
+        byte[] arrayA = TEST_DATA.getBytes();
+        assertEquals(cap.read(arrayA, 0, arrayA.length), -1);
+
+        cap.put(arrayA, 0, arrayA.length);
+
+        byte[] arrayB = new byte[arrayA.length];
+        cap.setForRead();
+        assertEquals(arrayA.length, cap.read(arrayB, 0, arrayB.length));
+        assertEquals(TEST_DATA, new String(arrayB));
+        assertEquals(-1, cap.read());
+        cap.done();
+
+        String b = "This is some content that we want to read";
+        byte[] a = b.getBytes();
+        byte[] c = new byte[b.length()]; // we want to use this to test reading offsets, etc
+
+        for (int i = 0; i < a.length; i += 11) {
+            cap.put(a, i, Math.min(11, a.length-i));
+        }
+        cap.reset();
+        int read;
+        for (int i = 0; i < c.length; i += read) {
+            read = cap.read(c, i, Math.min(3, c.length-i));
+        }
+        assertEquals(b, new String(c));
+    }
+
+    @Test
+    public void resetTest() throws Exception {
+        cap.reset();
+        Field curr_field = Capacitor.class.getDeclaredField("curr");
+        curr_field.setAccessible(true);
+        Field idx_field = Capacitor.class.getDeclaredField("idx");
+        idx_field.setAccessible(true);
+        assertNull(curr_field.get(cap));
+        assertEquals(idx_field.get(cap), 0);
+
+        cap.put((byte)'a');
+        cap.reset();
+        assertNotNull(curr_field.get(cap));
+        assertEquals(idx_field.get(cap), 1);
+    }
+
+    @Test
+    public void skipTest() throws Exception {
+        // capacitor can't skip if nothing has been put into it
+        assertEquals(cap.skip(10), 0);
+        cap.put((byte)'a');
+        // The Capacitor can hold 256 bytes. If we try  to skip 100 bytes,
+        // it should only skip 1 byte, leaving 255 remaining
+        assertEquals(cap.skip(100), 255);
+
+        // Skipping 200 bytes leaves 0 remaining
+        assertEquals(cap.skip(200), 0);
+    }
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_CmdLine.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_CmdLine.java
new file mode 100644 (file)
index 0000000..473aa1a
--- /dev/null
@@ -0,0 +1,275 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Properties;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.ccsdk.apps.cadi.CmdLine;
+import org.onap.ccsdk.apps.cadi.Symm;
+
+public class JU_CmdLine {
+
+    @Mock
+    private OutputStream thrower;
+
+    private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
+
+    private String password;
+    private String keyfile;
+    private String quickBrownFoxPlain = "The quick brown fox jumps over the lazy dog";
+    private String quickBrownFoxMD5 = "0x9e107d9d372bb6826bd81d3542a419d6";
+    private String quickBrownFoxSHA256 = "0xd7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592";
+    private Symm symm;
+
+    @Before
+    public void setup() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        System.setOut(new PrintStream(outContent));
+
+        Properties p = new Properties();
+        p.setProperty("force_exit", "false");
+
+        CmdLine.setSystemExit(false);
+        keyfile = "src/test/resources/keyfile";
+        password = "password";
+
+        File keyF = new File("src/test/resources", "keyfile");
+        FileInputStream fis = new FileInputStream(keyF);
+        try {
+            symm = Symm.obtain(fis);
+        } finally {
+            fis.close();
+        }
+    }
+
+    @After
+    public void restoreStreams() throws IOException {
+        System.setOut(System.out);
+        System.setIn(System.in);
+    }
+
+    @Test
+    public void digestTest() throws Exception {
+        CmdLine.main(new String[]{"digest", password, keyfile});
+        String decrypted = symm.depass(outContent.toString());
+        assertThat(decrypted, is(password));
+
+        System.setIn(new ByteArrayInputStream(password.getBytes()));
+        CmdLine.main(new String[]{"digest", "-i", keyfile});
+        decrypted = symm.depass(outContent.toString());
+        assertThat(decrypted, is(password));
+    }
+
+    @Test
+    public void encode64Test() throws Exception {
+        CmdLine.main(new String[]{"encode64", password});
+        String decrypted = Symm.base64.decode(outContent.toString());
+        assertThat(decrypted, is(password));
+    }
+
+    @Test
+    public void decode64Test() throws Exception {
+        String encrypted = Symm.base64.encode(password);
+        CmdLine.main(new String[]{"decode64", encrypted});
+        assertThat(outContent.toString(), is(password + System.lineSeparator()));
+    }
+
+    @Test
+    public void encode64urlTest() throws Exception {
+        CmdLine.main(new String[]{"encode64url", password});
+        String decrypted = Symm.base64url.decode(outContent.toString());
+        assertThat(decrypted, is(password));
+    }
+
+    @Test
+    public void decode64urlTest() throws Exception {
+        String encrypted = Symm.base64url.encode(password);
+        CmdLine.main(new String[]{"decode64url", encrypted});
+        assertThat(outContent.toString(), is(password  + System.lineSeparator()));
+    }
+
+    @Test
+    public void md5Test() throws Exception {
+        CmdLine.main(new String[]{"md5", quickBrownFoxPlain});
+        assertThat(outContent.toString(), is(quickBrownFoxMD5  + System.lineSeparator()));
+    }
+
+    @Test
+    public void sha256Test() throws Exception {
+        CmdLine.main(new String[]{"sha256", quickBrownFoxPlain});
+        assertThat(outContent.toString(), is(quickBrownFoxSHA256  + System.lineSeparator()));
+
+        outContent.reset();
+        CmdLine.main(new String[]{"sha256", quickBrownFoxPlain, "10"});
+        String hash1 = outContent.toString();
+
+        outContent.reset();
+        CmdLine.main(new String[]{"sha256", quickBrownFoxPlain, "10"});
+        String hash2 = outContent.toString();
+
+        outContent.reset();
+        CmdLine.main(new String[]{"sha256", quickBrownFoxPlain, "11"});
+        String hash3 = outContent.toString();
+
+        assertThat(hash1, is(hash2));
+        assertThat(hash1, is(not(hash3)));
+    }
+
+    @Test
+    public void keygenTest() throws Exception {
+        CmdLine.main(new String[]{"keygen"});
+        assertThat(outContent.toString().length(), is(2074));
+
+        String filePath = "test/output_key";
+        File testDir = new File("test");
+        if (!testDir.exists()) {
+            testDir.mkdirs();
+        }
+        CmdLine.main(new String[]{"keygen", filePath});
+        File keyfile = new File(filePath);
+        assertTrue(Files.isReadable(Paths.get(filePath)));
+        assertFalse(Files.isWritable(Paths.get(filePath)));
+        //assertFalse(Files.isExecutable(Paths.get(filePath)));
+        keyfile.delete();
+    }
+
+    @Test
+    public void passgenTest() throws Exception {
+        CmdLine.main(new String[]{"passgen"});
+        String output = outContent.toString().trim();
+        assertThat(output.length(), is(24));
+        assertTrue(containsAny(output, "+!@#$%^&*(){}[]?:;,."));
+        assertTrue(containsAny(output, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
+        assertTrue(containsAny(output, "abcdefghijklmnopqrstuvwxyz"));
+        assertTrue(containsAny(output, "0123456789"));
+
+        int length = 10;
+        outContent.reset();
+        CmdLine.main(new String[]{"passgen", String.valueOf(length)});
+        output = outContent.toString().trim();
+        assertThat(output.length(), is(length));
+
+        length = 5;
+        outContent.reset();
+        CmdLine.main(new String[]{"passgen", String.valueOf(length)});
+        output = outContent.toString().trim();
+        assertThat(output.length(), is(8));
+
+        // Check that the custom hasRepeats method works
+        assertTrue(hasRepeats("aa"));
+        assertTrue(hasRepeats("baa"));
+        assertTrue(hasRepeats("aab"));
+        assertTrue(hasRepeats("baab"));
+        assertFalse(hasRepeats("abc"));
+        assertFalse(hasRepeats("aba"));
+
+        // Run this a bunch of times for coverage
+        for (int i = 0; i < 1000; i++) {
+            outContent.reset();
+            CmdLine.main(new String[]{"passgen"});
+            output = outContent.toString().trim();
+            assertFalse(hasRepeats(output));
+        }
+    }
+
+    @Test
+    public void urlgenTest() throws Exception {
+        CmdLine.main(new String[]{"urlgen"});
+        String output = outContent.toString().trim();
+        assertThat(output.length(), is(24));
+
+        int length = 5;
+        outContent.reset();
+        CmdLine.main(new String[]{"urlgen", String.valueOf(length)});
+        output = outContent.toString().trim();
+        assertThat(output.length(), is(5));
+    }
+
+    @Test
+    public void showHelpTest() {
+        String lineSeparator = System.lineSeparator();
+        String expected =
+            "Usage: java -jar <this jar> ..." + lineSeparator +
+            "  keygen [<keyfile>]                     (Generates Key on file, or Std Out)" + lineSeparator +
+            "  digest [<passwd>|-i|] <keyfile>        (Encrypts Password with \"keyfile\"" + lineSeparator +
+            "                                          if passwd = -i, will read StdIn" + lineSeparator +
+            "                                          if passwd is blank, will ask securely)" + lineSeparator +
+            "  undigest <enc:...> <keyfile>           (Decrypts Encoded with \"keyfile\")" + lineSeparator +
+            "  passgen <digits>                       (Generate Password of given size)" + lineSeparator +
+            "  urlgen <digits>                        (Generate URL field of given size)" + lineSeparator +
+            "  encode64 <your text>                   (Encodes to Base64)" + lineSeparator +
+            "  decode64 <base64 encoded text>         (Decodes from Base64)" + lineSeparator +
+            "  encode64url <your text>                (Encodes to Base64 URL charset)" + lineSeparator +
+            "  decode64url <base64url encoded text>   (Decodes from Base64 URL charset)" + lineSeparator +
+            "  sha256 <text> <salts(s)>               (Digest String into SHA256 Hash)" + lineSeparator +
+            "  md5 <text>                             (Digest String into MD5 Hash)" + lineSeparator;
+
+        CmdLine.main(new String[]{});
+
+        assertThat(outContent.toString(), is(expected));
+    }
+
+    private boolean containsAny(String str, String searchChars) {
+        for (char c : searchChars.toCharArray()) {
+            if (str.indexOf(c) >= 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean hasRepeats(String str) {
+        int c = -1;
+        int last;
+        for (int i = 0; i < str.length(); i++) {
+            last = c;
+            c = str.charAt(i);
+            if (c == last) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_Hash.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_Hash.java
new file mode 100644 (file)
index 0000000..946d931
--- /dev/null
@@ -0,0 +1,225 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.test;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.ccsdk.apps.cadi.Hash;
+
+public class JU_Hash {
+    // Some common test vectors
+    private String quickBrownFoxVector = "The quick brown fox jumps over the lazy dog";
+    private String quickBrownFoxMD5 = "0x9e107d9d372bb6826bd81d3542a419d6";
+    private String quickBrownFoxSHA256 = "0xd7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592";
+
+    private String emptyVector = "";
+    private String emptyMD5 = "0xd41d8cd98f00b204e9800998ecf8427e";
+    private String emptySHA256 = "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
+
+
+    private byte[] same1 = "this is a twin".getBytes();
+    private byte[] same2 = "this is a twin".getBytes();
+    private byte[] different1 = "guvf vf n gjva".getBytes();
+    private byte[] different2 = "this is an only child".getBytes();
+
+
+    private String uppersDec = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+    private String uppersHex1 = "0x4142434445464748494A4B4C4D4E4F505152535455565758595A";
+    private String uppersHex2 = "0x4142434445464748494a4b4c4d4e4f505152535455565758595a";
+    private String uppersHexNo0x1 = "4142434445464748494a4b4c4d4e4f505152535455565758595a";
+    private String uppersHexNo0x2 = "4142434445464748494A4B4C4D4E4F505152535455565758595A";
+
+    private String lowersDec = "abcdefghijklmnopqrstuvwxyz";
+    private String lowersHex = "0x6162636465666768696a6b6c6d6e6f707172737475767778797a";
+    private String lowersHexNo0x1 = "6162636465666768696a6b6c6d6e6f707172737475767778797a";
+    private String lowersHexNo0x2 = "6162636465666768696A6B6C6D6E6F707172737475767778797A";
+
+    private String numbersDec = "1234567890";
+    private String numbersHex = "0x31323334353637383930";
+    private String numbersHexNo0x = "31323334353637383930";
+        
+    @SuppressWarnings("unused")
+    @BeforeClass
+    public static void getCoverage() {
+        // All of this class's methods are static, so we never need to instantiate an object.
+        // That said, we can't get 100% coverage unless we instantiate one
+        Hash hash = new Hash();
+    }
+
+    @Test
+    public void hashMD5Test() throws Exception {
+        byte[] output = Hash.hashMD5(quickBrownFoxVector.getBytes());
+        assertEquals(quickBrownFoxMD5, new String(Hash.toHex(output)));
+
+        output = Hash.hashMD5(emptyVector.getBytes());
+        assertEquals(emptyMD5, new String(Hash.toHex(output)));
+    }
+
+    @Test
+    public void hashMD5WithOffsetTest() throws Exception {
+        byte[] output = Hash.hashMD5(quickBrownFoxVector.getBytes(), 0, quickBrownFoxVector.length());
+        assertEquals(quickBrownFoxMD5, new String(Hash.toHex(output)));
+
+        output = Hash.hashMD5(emptyVector.getBytes(), 0, emptyVector.length());
+        assertEquals(emptyMD5, new String(Hash.toHex(output)));
+    }
+
+    @Test
+    public void hashMD5AsStringHexTest() throws Exception {
+        String output = Hash.hashMD5asStringHex(quickBrownFoxVector);
+        assertEquals(quickBrownFoxMD5, output);
+
+        output = Hash.hashMD5asStringHex(emptyVector);
+        assertEquals(emptyMD5, output);
+    }
+
+    @Test
+    public void hashSHA256Test() throws Exception {
+        byte[] output = Hash.hashSHA256(quickBrownFoxVector.getBytes());
+        assertEquals(quickBrownFoxSHA256, new String(Hash.toHex(output)));
+
+        output = Hash.hashSHA256(emptyVector.getBytes());
+        assertEquals(emptySHA256, new String(Hash.toHex(output)));
+    }
+
+    @Test
+    public void hashSHA256WithOffsetTest() throws Exception {
+        byte[] output = Hash.hashSHA256(quickBrownFoxVector.getBytes(), 0, quickBrownFoxVector.length());
+        assertEquals(quickBrownFoxSHA256, new String(Hash.toHex(output)));
+
+        output = Hash.hashSHA256(emptyVector.getBytes(), 0, emptyVector.length());
+        assertEquals(emptySHA256, new String(Hash.toHex(output)));
+    }
+
+    @Test
+    public void hashSHA256AsStringHexTest() throws Exception {
+        String output = Hash.hashSHA256asStringHex(quickBrownFoxVector);
+        assertEquals(quickBrownFoxSHA256, output);
+
+        output = Hash.hashSHA256asStringHex(emptyVector);
+        assertEquals(emptySHA256, output);
+    }
+
+    @Test
+    public void hashSaltySHA256AsStringHexTest() throws Exception {
+        String input = "password";
+        String hash1 = Hash.hashSHA256asStringHex(input, 10);
+        String hash2 = Hash.hashSHA256asStringHex(input, 10);
+        String hash3 = Hash.hashSHA256asStringHex(input, 11);
+
+        assertEquals(hash1, hash2);
+        assertThat(hash1, not(equalTo(hash3)));
+    }
+
+    @Test
+    public void isEqualTest() throws Exception {
+        assertTrue(Hash.isEqual(same1, same2));
+        assertFalse(Hash.isEqual(same1, different1));
+        assertFalse(Hash.isEqual(same1, different2));
+    }
+
+    @Test
+    public void compareToTest() throws Exception {
+        assertEquals(0, Hash.compareTo(same1, same2));
+        // different1 is rot13(same1), so the difference should be 13
+        assertEquals(13, Hash.compareTo(same1, different1));
+        assertEquals(-78, Hash.compareTo(same1, different2));
+    }
+
+    @Test
+    public void toHexNo0xTest() throws Exception {
+        assertEquals(uppersHexNo0x1, Hash.toHexNo0x(uppersDec.getBytes()));
+        assertEquals(lowersHexNo0x1, Hash.toHexNo0x(lowersDec.getBytes()));
+        assertEquals(numbersHexNo0x, Hash.toHexNo0x(numbersDec.getBytes()));
+    }
+
+    @Test
+    public void toHexTest() throws Exception {
+        assertEquals(uppersHex2, Hash.toHex(uppersDec.getBytes()));
+        assertEquals(lowersHex, Hash.toHex(lowersDec.getBytes()));
+        assertEquals(numbersHex, Hash.toHex(numbersDec.getBytes()));
+    }
+
+    @Test
+    public void toHexWithOffset() throws Exception {
+        assertEquals(uppersHex2, Hash.toHex(uppersDec.getBytes(), 0, uppersDec.length()));
+        assertEquals(lowersHex, Hash.toHex(lowersDec.getBytes(), 0, lowersDec.length()));
+        assertEquals(numbersHex, Hash.toHex(numbersDec.getBytes(), 0, numbersDec.length()));
+    }
+
+    @Test
+    public void fromHexTest() throws Exception {
+        assertEquals(uppersDec, new String(Hash.fromHex(uppersHex1)));
+        assertEquals(lowersDec, new String(Hash.fromHex(lowersHex)));
+        assertEquals(numbersDec, new String(Hash.fromHex(numbersHex)));
+
+        // This string doesn't begin with "0x"
+        assertNull(Hash.fromHex("0X65"));
+
+            // This string has invalid hex characters
+        assertNull(Hash.fromHex("0xQ"));
+    }
+
+    @Test
+    public void fromHexNo0xTest() throws Exception {
+        assertEquals(uppersDec, new String(Hash.fromHexNo0x(uppersHexNo0x1)));
+        assertEquals(lowersDec, new String(Hash.fromHexNo0x(lowersHexNo0x1)));
+        assertEquals(uppersDec, new String(Hash.fromHexNo0x(uppersHexNo0x2)));
+        assertEquals(lowersDec, new String(Hash.fromHexNo0x(lowersHexNo0x2)));
+        byte[] output = Hash.fromHexNo0x("ABC");
+        assertEquals(new String(new byte[] {(byte)0x0A, (byte)0xBC}), new String(output));
+        assertNull(Hash.fromHexNo0x("~~"));
+    }
+    
+    @Test
+    public void aaf_941() throws Exception {
+        // User notes: From reported error "aaf" not coded right for odd digits
+       // Note:  In the original concept, this isn't a valid Hex digit.  It has to do with whether to assume an initial 
+       // char of "0" if left out.
+       
+       String sample = "aaf";
+       byte[] bytes = Hash.fromHexNo0x(sample);
+       String back = Hash.toHexNo0x(bytes);
+       // Note: We don't presume to know that someone left off leading 0 on start.
+       assertEquals("0aaf", back);
+       
+       sample = "0x0aaf";
+       bytes = Hash.fromHex(sample);
+       back = Hash.toHex(bytes);
+       assertEquals(sample, back);
+       
+       // Assumed leading zero.  Note, we ALWAYS translate back with leading zero.  
+       bytes = Hash.fromHex("0xaaf");
+       back = Hash.toHex(bytes);
+       assertEquals(sample, back);
+
+    }
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_LocatorException.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_LocatorException.java
new file mode 100644 (file)
index 0000000..26c56c6
--- /dev/null
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.test;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.onap.ccsdk.apps.cadi.LocatorException;
+
+import static org.hamcrest.CoreMatchers.is;
+
+public class JU_LocatorException {
+    @Test
+    public void stringTest() {
+        LocatorException exception = new LocatorException("New Exception");
+        assertNotNull(exception);
+        assertThat(exception.getMessage(), is("New Exception"));
+    }
+
+    @Test
+    public void throwableTest() {
+        LocatorException exception = new LocatorException(new Throwable("New Exception"));
+        assertNotNull(exception);
+        assertThat(exception.getMessage(), is("java.lang.Throwable: New Exception"));
+    }
+
+    @Test
+    public void stringThrowableTest() {
+        LocatorException exception = new LocatorException("New Exception",new Throwable("New Exception"));
+        assertNotNull(exception);
+        assertThat(exception.getMessage(), is("New Exception"));
+    }
+
+    @Test
+    public void characterSequenceTest() {
+        CharSequence testCS = new String("New Exception");
+        LocatorException exception = new LocatorException(testCS);
+        assertNotNull(exception);
+        assertThat(exception.getMessage(), is("New Exception"));
+    }
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_PropAccess.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_PropAccess.java
new file mode 100644 (file)
index 0000000..198c94c
--- /dev/null
@@ -0,0 +1,146 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.test;
+
+import static org.junit.Assert.*;
+import org.junit.Test;
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.Access.Level;
+import org.onap.ccsdk.apps.cadi.PropAccess.LogIt;
+
+import static org.mockito.Mockito.*;
+import static org.hamcrest.CoreMatchers.*;
+
+import java.lang.reflect.Field;
+
+import java.io.ByteArrayInputStream;
+import java.io.PrintStream;
+import java.util.Properties;
+
+@SuppressWarnings("unused")
+public class JU_PropAccess {
+    // Note: We can't actually get coverage of the protected constructor -
+    // that will be done later, when testing the child class "ServletContextAccess"
+
+
+    @Test
+    public void ConstructorTest() throws Exception {
+        PropAccess prop = new PropAccess();
+        assertThat(prop.getProperties(), is(not(nullValue())));
+    }
+
+    @Test
+    public void noPrintStreamConstructionTest() throws Exception {
+        // Test for coverage
+        PropAccess prop = new PropAccess((PrintStream)null, new String[]{"Invalid argument"});
+    }
+
+    @Test
+    public void propertiesConstructionTest() throws Exception {
+        // Coverage tests
+        PropAccess prop = new PropAccess(System.getProperties());
+        prop = new PropAccess((PrintStream)null, System.getProperties());
+    }
+
+    @Test
+    public void stringConstructionTest() throws Exception {
+        Properties testSystemProps = new Properties(System.getProperties());
+        testSystemProps.setProperty("cadi_name", "user");
+        System.setProperties(testSystemProps);
+        PropAccess prop = new PropAccess("cadi_keyfile=src/test/resources/keyfile", "cadi_loglevel=DEBUG", "cadi_prop_files=test/cadi.properties:not_a_file");
+    }
+
+    @Test
+    public void loadTest() throws Exception {
+        // Coverage tests
+        Properties props = mock(Properties.class);
+        when(props.getProperty("cadi_prop_files")).thenReturn("test/cadi.properties").thenReturn(null);
+        PropAccess pa = new PropAccess();
+        Field props_field = PropAccess.class.getDeclaredField("props");
+        props_field.setAccessible(true);
+        props_field.set(pa, props);
+        ByteArrayInputStream bais = new ByteArrayInputStream(new byte[0]);
+        pa.load(bais);
+    }
+
+    @Test
+    public void specialConversionsTest() throws Exception {
+        // Coverage tests
+        Properties testSystemProps = new Properties(System.getProperties());
+        testSystemProps.setProperty("java.specification.version", "1.7");
+        System.setProperties(testSystemProps);
+        PropAccess pa = new PropAccess("AFT_LATITUDE=1", "AFT_LONGITUDE=1", "cadi_protocols=TLSv1.2");
+    }
+
+    @Test
+    public void logTest() throws Exception {
+        // Coverage tests
+        PropAccess pa = new PropAccess();
+
+        pa.log(Level.DEBUG);
+        pa.printf(Level.DEBUG, "not a real format string");
+
+        pa.setLogLevel(Level.DEBUG);
+        pa.log(Level.DEBUG);
+        pa.log(Level.DEBUG, 1, " ", null, "");
+        pa.log(Level.DEBUG, "This is a string", "This is another");
+        pa.set(new LogIt() {
+            @Override public void push(Level level, Object ... elements) {}
+        });
+        try {
+            pa.log(new Exception("This exception was thrown intentionally, please ignore it"));
+        } catch (Exception e) {
+            fail("Should have thrown an exception");
+        }
+    }
+
+    @Test
+    public void classLoaderTest() {
+        PropAccess pa = new PropAccess();
+        assertThat(pa.classLoader(), instanceOf(ClassLoader.class));
+    }
+
+    @Test
+    public void encryptionTest() throws Exception {
+        PropAccess pa = new PropAccess();
+        String plainText = "This is a secret message";
+        String secret_message = pa.encrypt(plainText);
+        String modified = secret_message.substring(4);
+        // Plenty of assertions to hit all branches
+        assertThat(pa.decrypt(secret_message, false), is(plainText));
+        assertThat(pa.decrypt(null, false), is(nullValue()));
+        assertThat(pa.decrypt(modified, true), is(plainText));
+        assertThat(pa.decrypt(modified, false), is(modified));
+    }
+
+    @Test
+    public void setPropertyTest() {
+        PropAccess pa = new PropAccess();
+        pa.setProperty("test", null);
+        String prop = "New Property";
+        String val ="And it's faithful value";
+        pa.setProperty(prop, val);
+
+        assertThat(pa.getProperty(prop), is(val));
+    }
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_ServletContextAccess.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_ServletContextAccess.java
new file mode 100644 (file)
index 0000000..250e5f8
--- /dev/null
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.test;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.ServletContextAccess;
+import org.onap.ccsdk.apps.cadi.Access.Level;
+import org.onap.ccsdk.apps.cadi.PropAccess.LogIt;
+
+import static org.mockito.Mockito.*;
+import static org.hamcrest.CoreMatchers.*;
+
+import java.lang.reflect.Field;
+
+import java.io.ByteArrayInputStream;
+import java.io.PrintStream;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletContext;
+
+@SuppressWarnings("unused")
+public class JU_ServletContextAccess {
+
+    private FilterConfig filter_mock;
+    Enumeration<String> enumeration;
+
+    private class CustomEnumeration implements Enumeration<String> {
+        private int idx = 0;
+        private final String[] elements = {"This", "is", "a", "test"};
+        @Override
+        public String nextElement() {
+            return idx >= elements.length ? null : elements[idx++];
+        }
+        @Override
+        public boolean hasMoreElements() {
+            return idx < elements.length;
+        }
+    }
+
+    @Before
+    public void setup() {
+        enumeration = new CustomEnumeration();
+        filter_mock = mock(FilterConfig.class);
+        when(filter_mock.getInitParameterNames()).thenReturn(enumeration);
+    }
+
+
+    @Test
+    public void logTest() throws Exception {
+        ServletContext sc_mock = mock(ServletContext.class);
+        when(filter_mock.getServletContext()).thenReturn(sc_mock);
+        ServletContextAccess sca = new ServletContextAccess(filter_mock);
+
+        sca.log(Level.DEBUG);
+
+        sca.setLogLevel(Level.DEBUG);
+        sca.log(Level.DEBUG);
+
+        try {
+            sca.log(new Exception("This exception was thrown intentionally, please ignore it"));
+        } catch (Exception e) {
+            fail("Should have thrown an exception");
+        }
+    }
+
+    @Test
+    public void contextTest() {
+        ServletContext sc_mock = mock(ServletContext.class);
+        when(filter_mock.getServletContext()).thenReturn(sc_mock);
+        ServletContextAccess sca = new ServletContextAccess(filter_mock);
+        assertThat(sca.context(), instanceOf(ServletContext.class));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_Symm.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_Symm.java
new file mode 100644 (file)
index 0000000..bdffe5a
--- /dev/null
@@ -0,0 +1,213 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.test;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+import java.lang.reflect.*;
+import org.junit.*;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.Arrays;
+
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.Symm;
+
+public class JU_Symm {
+    private Symm defaultSymm;
+
+    private ByteArrayOutputStream outStream;
+
+    @Before
+    public void setup() throws Exception {
+        defaultSymm = new Symm(
+                "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray()
+                ,76, "Use default!" ,true, "Junit 1");
+        outStream = new ByteArrayOutputStream();
+        System.setOut(new PrintStream(outStream));
+    }
+
+    @After
+    public void tearDown() {
+        System.setOut(System.out);
+    }
+
+    @Test
+    public void constructorTest() throws Exception {
+        Symm myCustomSymm = new Symm(
+            "ACEGIKMOQSUWYacegikmoqsuwy02468+/".toCharArray(), 76, "Default", true, "Junit 2");
+        Field convert_field = Symm.class.getDeclaredField("convert");
+        convert_field.setAccessible(true);
+
+        Class<?> Unordered_class = Class.forName("org.onap.ccsdk.apps.cadi.Symm$Unordered");
+        assertThat(convert_field.get(myCustomSymm), instanceOf(Unordered_class));
+    }
+
+    @SuppressWarnings("unused")
+    @Test
+    public void copyTest() throws Exception {
+        Symm copy = Symm.base64.copy(76);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void deprecatedTest() {
+        assertEquals(Symm.base64(), Symm.base64);
+        assertEquals(Symm.base64noSplit(), Symm.base64noSplit);
+        assertEquals(Symm.base64url(), Symm.base64url);
+        assertEquals(Symm.baseCrypt(), Symm.encrypt);
+    }
+
+    @Test
+    public void encodeDecodeStringTest() throws Exception {
+        String orig = "hello";
+        String b64encrypted = Symm.base64.encode(orig);
+        assertEquals(Symm.base64.decode(b64encrypted), orig);
+
+        String defaultEnrypted = defaultSymm.encode(orig);
+        assertEquals(defaultSymm.decode(defaultEnrypted), orig);
+    }
+
+    @Test
+    public void encodeDecodeByteArrayTest() throws Exception {
+        String orig = "hello";
+        byte[] b64encrypted = Symm.base64.encode(orig.getBytes());
+        assertEquals(new String(Symm.base64.decode(b64encrypted)), orig);
+
+        byte[] empty = null;
+        assertTrue(Arrays.equals(Symm.base64.encode(empty), new byte[0]));
+    }
+
+    @Test
+    public void encodeDecodeStringToStreamTest() throws Exception {
+        String orig = "I'm a password, really";
+        String b64encrypted;
+        String output;
+
+        ByteArrayOutputStream baosEncrypt = new ByteArrayOutputStream();
+        Symm.base64.encode(orig, baosEncrypt);
+        b64encrypted = new String(baosEncrypt.toByteArray());
+
+        ByteArrayOutputStream baosDecrypt = new ByteArrayOutputStream();
+        Symm.base64.decode(b64encrypted, baosDecrypt);
+        output = new String(baosDecrypt.toByteArray());
+
+        assertEquals(orig, output);
+    }
+
+    @Test
+    public void encryptDecryptStreamWithPrefixTest() throws Exception {
+        String orig = "I'm a password, really";
+        byte[] b64encrypted;
+        String output;
+
+        byte[] prefix = "enc:".getBytes();
+
+        ByteArrayInputStream baisEncrypt = new ByteArrayInputStream(orig.getBytes());
+        ByteArrayOutputStream baosEncrypt = new ByteArrayOutputStream();
+        Symm.base64.encode(baisEncrypt, baosEncrypt, prefix);
+
+        b64encrypted = baosEncrypt.toByteArray();
+
+        ByteArrayInputStream baisDecrypt = new ByteArrayInputStream(b64encrypted);
+        ByteArrayOutputStream baosDecrypt = new ByteArrayOutputStream();
+        Symm.base64.decode(baisDecrypt, baosDecrypt, prefix.length);
+
+        output = new String(baosDecrypt.toByteArray());
+        assertEquals(orig, output);
+    }
+
+    @Test
+    public void randomGenTest() {
+        // Ian - There really isn't a great way to test for randomness...
+        String prev = null;
+        for (int i = 0; i < 10; i++) {
+            String current = Symm.randomGen(100);
+            if (current.equals(prev)) {
+                fail("I don't know how, but you generated the exact same random string twice in a row");
+            }
+            prev = current;
+        }
+        assertTrue(true);
+    }
+
+    @Test
+    public void obtainTest() throws Exception {
+        Symm symm = Symm.base64.obtain();
+
+        String orig ="Another Password, please";
+        String encrypted = symm.enpass(orig);
+        String decrypted = symm.depass(encrypted);
+        assertEquals(orig, decrypted);
+    }
+
+    @Test
+    public void InputStreamObtainTest() throws Exception {
+        byte[] keygen = Symm.keygen();
+
+        Symm symm = Symm.obtain(new ByteArrayInputStream(keygen));
+
+        String orig ="Another Password, please";
+        String encrypted = symm.enpass(orig);
+        String decrypted = symm.depass(encrypted);
+        assertEquals(orig, decrypted);
+    }
+
+    @Test
+    public void StringObtainTest() throws Exception {
+        byte[] keygen = Symm.keygen();
+
+        Symm symm = Symm.obtain(new String(keygen));
+
+        String orig ="Another Password, please";
+        String encrypted = symm.enpass(orig);
+        String decrypted = symm.depass(encrypted);
+        assertEquals(orig, decrypted);
+    }
+
+    @Test
+    public void AccessObtainTest() throws Exception {
+        PropAccess pa = new PropAccess("cadi_keyfile=src/test/resources/keyfile");
+        Symm symm = Symm.obtain(pa);
+        String orig ="Another Password, please";
+        String encrypted = symm.enpass(orig);
+        String decrypted = symm.depass(encrypted);
+        assertEquals(orig, decrypted);
+
+        try {
+            PropAccess badPa = mock(PropAccess.class);
+            when(badPa.getProperty("cadi_keyfile", null)).thenReturn("not_a_real_file.txt");
+            symm = Symm.obtain(badPa);
+            fail("Should have thrown an exception");
+        } catch (CadiException e) {
+            assertTrue(e.getMessage().contains("ERROR: "));
+            assertTrue(e.getMessage().contains("not_a_real_file.txt"));
+            assertTrue(e.getMessage().contains(" does not exist!"));
+        }
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_TrustChecker.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_TrustChecker.java
new file mode 100644 (file)
index 0000000..dd018c1
--- /dev/null
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import org.onap.ccsdk.apps.cadi.TrustChecker;
+
+public class JU_TrustChecker {
+
+    @Test
+    public void noTrustTests() {
+        assertThat(TrustChecker.NOTRUST.mayTrust(null, null), is(nullValue()));
+        TrustChecker.NOTRUST.setLur(null);
+    }
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_User.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/test/JU_User.java
new file mode 100644 (file)
index 0000000..99b0160
--- /dev/null
@@ -0,0 +1,186 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.test;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.mockito.Mockito.when;
+
+import java.lang.reflect.Field;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.ccsdk.apps.cadi.Permission;
+import org.onap.ccsdk.apps.cadi.User;
+import org.onap.ccsdk.apps.cadi.lur.LocalPermission;
+
+public class JU_User {
+
+    private final Long SECOND = 1000L;
+    private final String name = "Fakey McFake";
+    private final String cred = "Fake credentials";
+
+    private Field perms_field;
+    private Field count_field;
+
+    @Mock
+    private Principal principal;
+
+    @Mock
+    private LocalPermission permission;
+    @Mock
+    private LocalPermission permission2;
+
+    @Before
+    public void setup() throws NoSuchFieldException, SecurityException {
+        MockitoAnnotations.initMocks(this);
+
+        when(principal.getName()).thenReturn("Principal");
+
+        when(permission.getKey()).thenReturn("NewKey");
+        when(permission.match(permission)).thenReturn(true);
+
+        when(permission2.getKey()).thenReturn("NewKey2");
+        when(permission2.match(permission)).thenReturn(false);
+
+        perms_field = User.class.getDeclaredField("perms");
+        perms_field.setAccessible(true);
+
+        count_field = User.class.getDeclaredField("count");
+        count_field.setAccessible(true);
+    }
+
+    @Test
+    public void constructorPrincipalTest() throws IllegalArgumentException, IllegalAccessException {
+        User<Permission> user = new User<Permission>(principal);
+        assertThat(user.name, is(principal.getName()));
+        assertThat(user.principal, is(principal));
+        assertThat(user.permExpires(), is(Long.MAX_VALUE));
+        assertThat((int)count_field.get(user), is(0));
+    }
+
+    @Test
+    public void constructorNameCredTest() throws IllegalArgumentException, IllegalAccessException {
+        User<Permission> user = new User<Permission>(name, cred.getBytes());
+        assertThat(user.name, is(name));
+        assertThat(user.principal, is(nullValue()));
+        assertThat(user.permExpires(), is(Long.MAX_VALUE));
+        assertThat((int)count_field.get(user), is(0));
+        assertThat(user.getCred(), is(cred.getBytes()));
+    }
+
+    @Test
+    public void constructorPrincipalIntervalTest() throws IllegalArgumentException, IllegalAccessException {
+        User<Permission> user = new User<Permission>(principal, 61 * SECOND);
+        Long approxExpiration = System.currentTimeMillis() + 61 * SECOND;
+        assertThat(user.name, is(principal.getName()));
+        assertThat(user.principal, is(principal));
+        assertTrue(Math.abs(user.permExpires() - approxExpiration) < 10L);
+        assertThat((int)count_field.get(user), is(0));
+    }
+
+    @Test
+    public void constructorNameCredIntervalTest() throws IllegalArgumentException, IllegalAccessException {
+        String name = "Fakey McFake";
+        User<Permission> user = new User<Permission>(name, cred.getBytes(), 61 * SECOND);
+        Long approxExpiration = System.currentTimeMillis() + 61 * SECOND;
+        assertThat(user.name, is(name));
+        assertThat(user.principal, is(nullValue()));
+        assertTrue(Math.abs(user.permExpires() - approxExpiration) < 10L);
+        assertThat((int)count_field.get(user), is(0));
+        assertThat(user.getCred(), is(cred.getBytes()));
+    }
+
+    @Test
+    public void countCheckTest() throws IllegalArgumentException, IllegalAccessException {
+        User<Permission> user = new User<Permission>(principal);
+        user.resetCount();
+        assertThat((int)count_field.get(user), is(0));
+        user.incCount();
+        assertThat((int)count_field.get(user), is(1));
+        user.incCount();
+        assertThat((int)count_field.get(user), is(2));
+        user.resetCount();
+        assertThat((int)count_field.get(user), is(0));
+    }
+
+    @Test
+    public void permTest() throws InterruptedException, IllegalArgumentException, IllegalAccessException {
+        User<Permission> user = new User<Permission>(principal);
+        assertThat(user.permExpires(), is(Long.MAX_VALUE));
+        user.renewPerm();
+        Thread.sleep(1);  // Let it expire
+        assertThat(user.permExpired(), is(true));
+
+        user = new User<Permission>(principal,100);
+        assertTrue(user.noPerms());
+        user.add(permission);
+        assertFalse(user.permsUnloaded());
+        assertFalse(user.noPerms());
+        user.setNoPerms();
+        assertThat(user.permExpired(), is(false));
+        assertTrue(user.permsUnloaded());
+        assertTrue(user.noPerms());
+        perms_field.set(user, null);
+        assertTrue(user.permsUnloaded());
+        assertTrue(user.noPerms());
+    }
+
+    @Test
+    public void addValuesToNewMapTest() {
+        User<Permission> user = new User<Permission>(principal);
+        Map<String, Permission> newMap = new HashMap<>();
+
+        assertFalse(user.contains(permission));
+
+        user.add(newMap, permission);
+        user.setMap(newMap);
+
+        assertTrue(user.contains(permission));
+
+        List<Permission> sink = new ArrayList<>();
+        user.copyPermsTo(sink);
+
+        assertThat(sink.size(), is(1));
+        assertTrue(sink.contains(permission));
+
+        assertThat(user.toString(), is("Principal|:NewKey"));
+
+        user.add(newMap, permission2);
+        user.setMap(newMap);
+        assertFalse(user.contains(permission2));
+
+        assertThat(user.toString(), is("Principal|:NewKey2,NewKey"));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_CSV.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_CSV.java
new file mode 100644 (file)
index 0000000..5be4a8c
--- /dev/null
@@ -0,0 +1,125 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.ccsdk
+ * ===========================================================================
+ * Copyright (c) 2023 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ */
+
+package org.onap.ccsdk.apps.cadi.util.test;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.ccsdk.apps.cadi.Access;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.PropAccess;
+import org.onap.ccsdk.apps.cadi.util.CSV;
+import org.onap.ccsdk.apps.cadi.util.CSV.Visitor;
+import org.onap.ccsdk.apps.cadi.util.CSV.Writer;
+import org.onap.ccsdk.apps.cadi.util.Holder;
+
+public class JU_CSV {
+
+    private String filename;
+    private File file;
+    private static ArrayList<Object> expected;
+
+    @Before
+    public void start() {
+        filename = "Sample.csv";
+        file = new File(filename);
+    }
+
+    @After
+    public void end() {
+        if(file!=null) {
+            file.delete();
+        }
+    }
+
+    @BeforeClass
+    public static void before() {
+        expected = new ArrayList<>();
+    }
+
+    @Test
+    public void test() throws IOException, CadiException {
+        Access access = new PropAccess();
+        CSV csv = new CSV(access,file);
+        // Can't visit for file that doesn't exist
+        try {
+            csv.visit(new Visitor() {
+                @Override
+                public void visit(List<String> row) {
+                }});
+        } catch(IOException e) {
+            Assert.assertTrue("CSV correctly created exception",true);
+        }
+
+        Writer writer = csv.writer();
+        try {
+            writer.row(add("\"hello\""));
+            writer.comment("Ignore Comments");
+            writer.row(add("dXNlcjpwYXNzd29yZA=="),add("dXNlckBzb21ldGhpbmcub3JnOm90aGVyUGFzc3dvcmQ="));
+            writer.row(); // no output
+            writer.row(add("There is, but one thing to say"), add(" and that is"), add("\"All the best\""));
+        } finally {
+            writer.close();
+        }
+
+        PrintStream garbage = new PrintStream(new FileOutputStream(file, true));
+        try {
+            garbage.println("# Ignore empty spaces, etc");
+            garbage.println("   ");
+            garbage.println("# Ignore blank lines");
+            garbage.println();
+        } finally {
+            garbage.close();
+        }
+
+
+    ////////////
+    // Tests
+    ////////////
+        final Holder<Integer> hi = new Holder<>(0);
+        csv.visit(new CSV.Visitor() {
+            @Override
+            public void visit(List<String> row) {
+                for(String s: row) {
+//                    System.out.println(hi.value + ") " + s);
+                    Assert.assertEquals(expected.get(hi.get()),s);
+                    hi.set(hi.get()+1); // increment
+                }
+            }
+        });
+
+    }
+
+    private String add(String s) {
+        expected.add(s);
+        return s;
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_Chmod.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_Chmod.java
new file mode 100644 (file)
index 0000000..770b874
--- /dev/null
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.util.test;
+
+import static org.junit.Assert.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.util.Set;
+
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import org.onap.ccsdk.apps.cadi.util.Chmod;
+
+public class JU_Chmod {
+
+    private File file;
+    private String filePath;
+
+    @Before
+    public void setup() throws IOException {
+        file = File.createTempFile("chmod_test", "");
+        filePath = file.getAbsolutePath();
+    }
+
+    @After
+    public void tearDown() {
+        file.delete();
+    }
+
+    @Test
+    public void to755Test() throws IOException {
+        Chmod.to755.chmod(file);
+        Set<PosixFilePermission> set = Files.getPosixFilePermissions(Paths.get(filePath));
+        assertThat(PosixFilePermissions.toString(set), is("rwxr-xr-x"));
+    }
+
+    @Test
+    public void to644Test() throws IOException {
+        Chmod.to644.chmod(file);
+        Set<PosixFilePermission> set = Files.getPosixFilePermissions(Paths.get(filePath));
+        assertThat(PosixFilePermissions.toString(set), is("rw-r--r--"));
+    }
+
+    @Test
+    public void to400Test() throws IOException {
+        Chmod.to400.chmod(file);
+        Set<PosixFilePermission> set = Files.getPosixFilePermissions(Paths.get(filePath));
+        assertThat(PosixFilePermissions.toString(set), is("r--------"));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_FQI.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_FQI.java
new file mode 100644 (file)
index 0000000..9c62d6d
--- /dev/null
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.util.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import org.onap.ccsdk.apps.cadi.util.FQI;
+
+public class JU_FQI {
+
+    @Test
+    public void reverseDomainTest() {
+        assertThat(FQI.reverseDomain("user@att.com"), is("com.att"));
+    }
+
+    @Test
+    public void coverageTest() {
+        @SuppressWarnings("unused")
+        FQI fqi = new FQI();
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_Holder.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_Holder.java
new file mode 100644 (file)
index 0000000..aaa42ad
--- /dev/null
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.util.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import org.junit.Test;
+import org.onap.ccsdk.apps.cadi.util.Holder;
+
+public class JU_Holder {
+
+    @Test
+    public void test() {
+        String str1 = "a string";
+        String str2 = "another string";
+        Holder<String> holder = new Holder<String>(str1);
+        assertThat(holder.get(), is(str1));
+        assertThat(holder.toString(), is(str1));
+
+        holder.set(str2);
+        assertThat(holder.get(), is(str2));
+        assertThat(holder.toString(), is(str2));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_JsonOutputStream.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_JsonOutputStream.java
new file mode 100644 (file)
index 0000000..1d74b16
--- /dev/null
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.util.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+
+import java.io.ByteArrayOutputStream;
+
+import org.junit.*;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+
+import org.onap.ccsdk.apps.cadi.util.JsonOutputStream;
+
+public class JU_JsonOutputStream {
+
+    private JsonOutputStream jos;
+
+    @Before
+    public void setup() {
+        jos = new JsonOutputStream(new ByteArrayOutputStream());
+    }
+
+    @Test
+    public void constructorTest() {
+        jos = new JsonOutputStream(System.out);
+        jos = new JsonOutputStream(System.err);
+    }
+
+    @Test
+    public void writeTest() throws IOException {
+        byte[] json = ("{" +
+                         "name: user," +
+                         "password: pass," +
+                         "contact: {" +
+                           "email: user@att.com," +
+                           "phone: 555-5555" +
+                         "}," +
+                         "list: [" +
+                           "item1," +
+                           "item2" +
+                         "],[],{}," +
+                         "list:" +
+                         "[" +
+                           "item1," +
+                           "item2" +
+                         "]" +
+                       "}").getBytes();
+        jos.write(json);
+    }
+
+    @Test
+    public void resetIndentTest() throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
+        Field indentField = JsonOutputStream.class.getDeclaredField("indent");
+        indentField.setAccessible(true);
+
+        assertThat((int)indentField.get(jos), is(0));
+        jos.resetIndent();
+        assertThat((int)indentField.get(jos), is(1));
+    }
+
+    @Test
+    public void coverageTest() throws IOException {
+        jos.flush();
+        jos.close();
+
+        jos = new JsonOutputStream(System.out);
+        jos.close();
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_MaskFormatException.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_MaskFormatException.java
new file mode 100644 (file)
index 0000000..8024032
--- /dev/null
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.util.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import org.onap.ccsdk.apps.cadi.util.MaskFormatException;
+
+public class JU_MaskFormatException {
+
+    @Test
+    public void throwsTest() {
+        String errorMessage = "This is a MaskFormatException";
+        try {
+            throw new MaskFormatException(errorMessage);
+        } catch (Exception e) {
+            assertThat(e.getMessage(), is(errorMessage));
+            assertTrue(e instanceof MaskFormatException);
+        }
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_NetMask.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_NetMask.java
new file mode 100644 (file)
index 0000000..c8b3da0
--- /dev/null
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.util.test;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.onap.ccsdk.apps.cadi.util.NetMask;
+
+public class JU_NetMask {
+
+    @Test
+    public void deriveTest() {
+        String test = "test";
+        assertEquals(NetMask.derive(test.getBytes()), 0);
+    }
+
+    @Test
+    public void deriveTest2() {
+        String test = "1.2.3.4";
+        assertEquals(NetMask.derive(test.getBytes()), 0);
+    }
+
+    @Test
+    public void deriveTest3() {
+        String test = "1.2.4";
+        assertEquals(NetMask.derive(test.getBytes()), 0);
+    }
+
+    @Test
+    public void deriveTest4() {
+        String test = "1.3.4";
+        assertEquals(NetMask.derive(test.getBytes()), 0);
+    }
+
+    @Test
+    public void deriveTest5() {
+        String test = "2.3.4";
+        assertEquals(NetMask.derive(test.getBytes()), 0);
+    }
+
+    @Test
+    public void deriveTest6() {
+        String test = "3.4";
+        assertEquals(NetMask.derive(test.getBytes()), 0);
+    }
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_Pool.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_Pool.java
new file mode 100644 (file)
index 0000000..3b15e46
--- /dev/null
@@ -0,0 +1,238 @@
+/*******************************************************************************
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.util.test;
+
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+import org.onap.ccsdk.apps.cadi.CadiException;
+import org.onap.ccsdk.apps.cadi.util.Log;
+import org.onap.ccsdk.apps.cadi.util.Pool;
+import org.onap.ccsdk.apps.cadi.util.Pool.*;
+
+public class JU_Pool {
+
+    private class IntegerCreator implements Creator<Integer> {
+        private int current = 0;
+
+        @Override
+        public Integer create() {
+            return current++;
+        }
+
+        @Override
+        public void destroy(Integer t) {
+            t = 0;
+        }
+
+        @Override
+        public boolean isValid(Integer t) {
+            return (t & 0x1) == 0;
+        }
+
+        @Override
+        public void reuse(Integer t) {
+        }
+    }
+
+    // Used for CustomLogger Testing
+    private StringBuilder sb = new StringBuilder();
+
+    private class CustomLogger implements Log {
+        @Override
+        public void log(Log.Type type, Object... o) {
+            for (Object item : o) {
+                sb.append(item.toString());
+            }
+        }
+    }
+
+    /**
+        * Enter variable amount in this order 
+        * 
+        *   count, used, max_range, max_objects
+        * @param intPool
+        * @param ints
+        */
+       private void check(Pool<Integer> intPool, int ... ints) {
+               String rpt = intPool.toString();
+               // Fallthrough on purpose, to process only the ints entered, but in the right order.
+               switch(ints.length) {
+                       case 4:
+                       assertTrue(rpt.contains(String.format("max_objects(%d)", ints[3])));
+                       case 3:
+                       assertTrue(rpt.contains(String.format("max_range(%d)", ints[2])));
+                       case 2:
+                       assertTrue(rpt.contains(String.format("used(%d)", ints[1])));
+                       case 1:
+                               assertTrue(rpt.contains(String.format("count(%d)", ints[0])));
+               }
+       }
+
+       @Test
+    public void settings() throws CadiException {
+       Pool<Integer> intPool = new Pool<Integer>(new IntegerCreator());
+       check(intPool,0,0,Pool.MAX_RANGE,Pool.MAX_OBJECTS);
+
+       // Check MaxObjects, min is 0
+       intPool.setMaxObjects(-10);
+       check(intPool,0,0,Pool.MAX_RANGE,0);
+
+       intPool.setMaxObjects(10);
+       check(intPool,0,0,Pool.MAX_RANGE,10);
+
+       // Check MaxRange, min is 0
+       intPool.setMaxRange(-10);
+       check(intPool,0,0,0,10);
+
+       intPool.setMaxRange(2);
+       check(intPool,0,0,2,10);
+
+       // Validate Priming
+       intPool.prime(3);
+       check(intPool,3,3,2,10);
+       
+       // Drain 
+       intPool.drain();
+       check(intPool,0,0,2,10);
+    }
+    
+    @Test
+    public void range() throws CadiException {
+       Pool<Integer> intPool = new Pool<Integer>(new IntegerCreator());
+       intPool.setMaxRange(2); 
+       check(intPool,0,0,2);
+       
+       // Prime
+       intPool.prime(3);
+       check(intPool,3,3,2);
+       
+       // Using 3 leaves count (in Pool) and Used (by System) 3
+       List<Pooled<Integer>> using = new ArrayList<>();
+       for(int i=0;i<3;++i) {
+               using.add(intPool.get());
+       }
+       check(intPool,0,3,2);
+
+       // Using 3 more creates more Objects, and uses immediately
+       for(int i=0;i<3;++i) {
+               using.add(intPool.get());
+       }
+       check(intPool,0,6,2);
+       
+       // Clean out all Objects in possession, but there are 6 Objects not returned yet.
+       intPool.drain();
+       check(intPool,0,6,2);
+       
+       // Returning Objects 
+       for(Pooled<Integer> i : using)  {
+               i.done();
+       }
+       
+       // Since Range is 2, keep only 2, and destroy the rest
+       check(intPool,2,2,2);
+
+       // Shutdown (helpful for stopping Services) involves turning off range
+       intPool.setMaxRange(0).drain();
+       check(intPool,0,0,0);
+    }
+    
+    @Test
+       public void tooManyObjects() throws CadiException {
+       /*
+        * It should be noted that "tooManyObjects" isn't enforced by the Pool, because Objects are not 
+        * tracked (other than used) once they leave the pool.  
+        * 
+        * It is information that using entities, like Thread Pools, can use to limit creations of expensive objects
+        */
+               Pool<Integer> intPool = new Pool<Integer>(new IntegerCreator());
+               intPool.setMaxObjects(10).setMaxRange(2);
+               check(intPool,0,0,2,10);
+
+               assertFalse(intPool.tooManyObjects());
+
+               // Obtain up to maxium Objects
+               List<Pooled<Integer>> using = new ArrayList<>();
+               for(int i=0;i<10;++i) {
+                       using.add(intPool.get());
+               }
+               
+               check(intPool,0,10,2,10);
+               assertFalse(intPool.tooManyObjects());
+               
+               using.add(intPool.get());
+               check(intPool,0,11,2,10);
+               assertTrue(intPool.tooManyObjects());
+               
+       // Returning Objects 
+       for(Pooled<Integer> i : using)  {
+               i.done();
+       }
+       
+       // Returning Objects puts Pool back in range
+               check(intPool,2,2,2,10);
+               assertFalse(intPool.tooManyObjects());
+
+       }
+
+       @Test
+    public void bulkTest() throws CadiException {
+        Pool<Integer> intPool = new Pool<Integer>(new IntegerCreator());
+
+        intPool.prime(10);
+        // Remove all of the invalid items (in this case, odd numbers)
+        assertFalse(intPool.validate());
+
+        // Make sure we got them all
+        assertTrue(intPool.validate());
+
+        // Get an item from the pool
+        Pooled<Integer> gotten = intPool.get();
+        assertThat(gotten.content, is(0));
+
+        // finalize that item, then check the next one to make sure we actually purged
+        // the odd numbers
+        gotten = intPool.get();
+        assertThat(gotten.content, is(2));
+
+        intPool.drain();
+
+    }
+
+    @Test
+    public void loggingTest() {
+        Pool<Integer> intPool = new Pool<Integer>(new IntegerCreator());
+
+        // Log to Log.NULL for coverage
+        intPool.log(Log.Type.info,"Test log output");
+
+        intPool.setLogger(new CustomLogger());
+        intPool.log(Log.Type.info,"Test log output");
+
+        assertThat(sb.toString(), is("Test log output"));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_Split.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_Split.java
new file mode 100644 (file)
index 0000000..a79dade
--- /dev/null
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.util.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import org.onap.ccsdk.apps.cadi.util.Split;
+
+public class JU_Split {
+
+    @Test
+    public void splitTest() {
+        String[] output = Split.split('c', "ctestctc", 0, "ctestctc".length());
+        assertThat(output.length, is(4));
+        assertThat(output[0], is(""));
+        assertThat(output[1], is("test"));
+        assertThat(output[2], is("t"));
+        assertThat(output[3], is(""));
+
+        output = Split.split('c', "ctestctc", 0, 4);
+        assertThat(output.length, is(2));
+        assertThat(output[0], is(""));
+        assertThat(output[1], is("tes"));
+
+        output = Split.split('c', "test", 0, "test".length());
+        assertThat(output.length, is(1));
+        assertThat(output[0], is("test"));
+
+        assertThat(Split.split('c', null, 0, 0).length, is(0));
+
+        // Test with fewer arguments
+        output = Split.split('c', "ctestctc");
+        assertThat(output.length, is(4));
+        assertThat(output[0], is(""));
+        assertThat(output[1], is("test"));
+        assertThat(output[2], is("t"));
+        assertThat(output[3], is(""));
+    }
+
+    @Test
+    public void splitTrimTest() {
+        String[] output = Split.splitTrim('c', " cte stc ctc ", 0, " cte stc ctc ".length());
+        assertThat(output.length, is(5));
+        assertThat(output[0], is(""));
+        assertThat(output[1], is("te st"));
+        assertThat(output[2], is(""));
+        assertThat(output[3], is("t"));
+        assertThat(output[4], is(""));
+
+        output = Split.splitTrim('c', " cte stc ctc ", 0, 5);
+        assertThat(output.length, is(2));
+        assertThat(output[0], is(""));
+        assertThat(output[1], is("te"));
+
+        assertThat(Split.splitTrim('c', " te st ", 0, " te st ".length())[0], is("te st"));
+
+        assertThat(Split.splitTrim('c', null, 0, 0).length, is(0));
+
+        // Test with 2 arguments
+        output = Split.splitTrim('c', " cte stc ctc ");
+        assertThat(output.length, is(5));
+        assertThat(output[0], is(""));
+        assertThat(output[1], is("te st"));
+        assertThat(output[2], is(""));
+        assertThat(output[3], is("t"));
+        assertThat(output[4], is(""));
+
+        // Tests with 1 argument
+        output = Split.splitTrim('c', " cte stc ctc ", 1);
+        assertThat(output.length, is(1));
+        assertThat(output[0], is("cte stc ctc"));
+
+        output = Split.splitTrim('c', "testctest2", 2);
+        assertThat(output.length, is(2));
+        assertThat(output[0], is("test"));
+        assertThat(output[1], is("test2"));
+
+        output = Split.splitTrim('c', " cte stc ctc ", 4);
+        assertThat(output.length, is(4));
+        assertThat(output[0], is(""));
+        assertThat(output[1], is("te st"));
+        assertThat(output[2], is(""));
+
+        assertThat(Split.splitTrim('c', null, 0).length, is(0));
+    }
+
+    @Test
+    public void coverageTest() {
+        @SuppressWarnings("unused")
+        Split split = new Split();
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_SubStandardConsole.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_SubStandardConsole.java
new file mode 100644 (file)
index 0000000..5f5bb57
--- /dev/null
@@ -0,0 +1,126 @@
+/*******************************************************************************
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.util.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import static org.mockito.Mockito.*;
+import org.junit.*;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.reflect.Field;
+
+import org.onap.ccsdk.apps.cadi.util.SubStandardConsole;
+
+public class JU_SubStandardConsole {
+
+    private String inputString = "An input string";
+    private ByteArrayOutputStream outStream;
+    private ByteArrayOutputStream errStream;
+    private String lineSeparator = System.lineSeparator();
+
+    @Before
+    public void setup() {
+        outStream = new ByteArrayOutputStream();
+        errStream = new ByteArrayOutputStream();
+        System.setOut(new PrintStream(outStream));
+        System.setErr(new PrintStream(errStream));
+    }
+
+    @After
+    public void tearDown() {
+        System.setOut(System.out);
+        System.setErr(System.err);
+    }
+
+    @Test
+    public void readLineTest() {
+        byte[] input = inputString.getBytes();
+        System.setIn(new ByteArrayInputStream(input));
+        SubStandardConsole ssc = new SubStandardConsole();
+        String output = ssc.readLine("%s" + lineSeparator, ">>> ");
+        assertThat(output, is(inputString));
+        assertThat(outStream.toString(), is(">>> " + lineSeparator));
+    }
+
+    @Test
+    public void readLineTest2() {
+        byte[] input = inputString.getBytes();
+        System.setIn(new ByteArrayInputStream(input));
+        SubStandardConsole ssc = new SubStandardConsole();
+        String output = ssc.readLine("%s %s"  + lineSeparator, ">>> ", "Another argument for coverage");
+        assertThat(output, is(inputString));
+    }
+
+    @Test
+    public void readLineTest3() {
+        byte[] input = "\n".getBytes();
+        System.setIn(new ByteArrayInputStream(input));
+        SubStandardConsole ssc = new SubStandardConsole();
+        String output = ssc.readLine("%s" + lineSeparator, ">>> ");
+        assertThat(output, is(">>> "));
+        assertThat(outStream.toString(), is(">>> " + lineSeparator));
+    }
+
+    @Test
+    public void readPasswordTest() {
+        byte[] input = inputString.getBytes();
+        System.setIn(new ByteArrayInputStream(input));
+        SubStandardConsole ssc = new SubStandardConsole();
+        char[] output = ssc.readPassword("%s" + lineSeparator, ">>> ");
+        System.out.println(output);
+        assertThat(output, is(inputString.toCharArray()));
+        assertThat(outStream.toString(), is(">>> " + lineSeparator + "An input string"  + lineSeparator));
+    }
+
+    @Test
+    public void printfTest() {
+        byte[] input = inputString.getBytes();
+        System.setIn(new ByteArrayInputStream(input));
+        SubStandardConsole ssc = new SubStandardConsole();
+        ssc.printf("%s", "A format specifier");
+        assertThat(outStream.toString(), is("A format specifier"));
+    }
+
+    @Test
+    public void throwsTest() throws IOException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        BufferedReader brMock = mock(BufferedReader.class);
+        when(brMock.readLine()).thenThrow(new IOException());
+
+        SubStandardConsole ssc = new SubStandardConsole();
+
+        Field brField = SubStandardConsole.class.getDeclaredField("br");
+        brField.setAccessible(true);
+        brField.set(ssc, brMock);
+
+        assertThat(ssc.readLine(""), is(""));
+        assertThat(errStream.toString(), is("uh oh..." + lineSeparator));
+        errStream.reset();
+        assertThat(ssc.readPassword("").length, is(0));
+        assertThat(errStream.toString(), is("uh oh..." + lineSeparator));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_TheConsole.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_TheConsole.java
new file mode 100644 (file)
index 0000000..f4fca0f
--- /dev/null
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.util.test;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.onap.ccsdk.apps.cadi.util.TheConsole;
+
+public class JU_TheConsole {
+
+    @Test
+    public void implemented(){
+        assertEquals(TheConsole.implemented(),false);
+    }
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_UserChainManip.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_UserChainManip.java
new file mode 100644 (file)
index 0000000..607bcc1
--- /dev/null
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.util.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.*;
+
+import org.onap.ccsdk.apps.cadi.UserChain;
+import org.onap.ccsdk.apps.cadi.util.UserChainManip;
+
+public class JU_UserChainManip {
+
+    @Test
+    public void build(){
+        UserChain.Protocol baseAuth=UserChain.Protocol.BasicAuth;
+        StringBuilder sb = UserChainManip.build(new StringBuilder(""), "app", "id", baseAuth, true);
+        assertThat(sb.toString(), is("app:id:BasicAuth:AS"));
+
+        // for coverage
+        sb = UserChainManip.build(sb, "app", "id", baseAuth, true);
+        assertThat(sb.toString(), is("app:id:BasicAuth:AS,app:id:BasicAuth"));
+
+        sb = UserChainManip.build(new StringBuilder(""), "app", "id", baseAuth, false);
+        assertThat(sb.toString(), is("app:id:BasicAuth"));
+    }
+
+    @Test
+    public void idToNSTEST() {
+        assertThat(UserChainManip.idToNS(null), is(""));
+        assertThat(UserChainManip.idToNS(""), is(""));
+        assertThat(UserChainManip.idToNS("something"), is(""));
+        assertThat(UserChainManip.idToNS("something@@"), is(""));
+        assertThat(UserChainManip.idToNS("something@@."), is(""));
+        assertThat(UserChainManip.idToNS("something@com"), is("com"));
+        assertThat(UserChainManip.idToNS("something@random.com"), is("com.random"));
+        assertThat(UserChainManip.idToNS("@random.com"), is("com.random"));
+        assertThat(UserChainManip.idToNS("something@random.com."), is("com.random"));
+        assertThat(UserChainManip.idToNS("something@..random...com..."), is("com.random"));
+        assertThat(UserChainManip.idToNS("something@this.random.com"), is("com.random.this"));
+    }
+
+    @Test
+    public void coverageTest() {
+        @SuppressWarnings("unused")
+        UserChainManip ucm = new UserChainManip();
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_Vars.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/util/test/JU_Vars.java
new file mode 100644 (file)
index 0000000..6b31ef0
--- /dev/null
@@ -0,0 +1,149 @@
+/*******************************************************************************
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.util.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+import org.onap.ccsdk.apps.cadi.util.Vars;
+
+public class JU_Vars {
+
+    @Test
+    public void coverage() {
+        @SuppressWarnings("unused")
+        Vars my_nonstatic_object_for_coverage = new Vars();
+    }
+
+    @Test
+    public void convert() {
+        String test = "test";
+        List<String> list = new ArrayList<>();
+        list.add("method");
+        assertEquals(Vars.convert(test, list), test);
+    }
+
+    @Test
+    public void convertTest1() {
+        List<String> list = new ArrayList<>();
+        list.add("method");
+        assertEquals(Vars.convert("test", list), "test");
+    }
+
+    @Test
+    public void convertTest2() {
+        List<String> list = new ArrayList<>();
+        list.add("method");
+        assertEquals(Vars.convert("test", list), "test");
+    }
+
+    @Test
+    public void test() {
+        StringBuilder holder = new StringBuilder();
+        String str,bstr;
+        assertEquals(str = "set %1 to %2",Vars.convert(holder,str, "a","b"));
+        assertEquals("set a to b",holder.toString());
+        assertEquals(str,Vars.convert(null,str, "a","b"));
+        holder.setLength(0);
+        assertEquals(str,Vars.convert(holder,bstr="set %s to %s", "a","b"));
+        assertEquals("set a to b",holder.toString());
+        assertEquals(str,Vars.convert(null,bstr, "a","b"));
+
+        holder.setLength(0);
+        assertEquals(str = "%1=%2",Vars.convert(holder,str, "a","b"));
+        assertEquals("a=b",holder.toString());
+        assertEquals(str,Vars.convert(null,str, "a","b"));
+        holder.setLength(0);
+        assertEquals(str,Vars.convert(holder,bstr="%s=%s", "a","b"));
+        assertEquals("a=b",holder.toString());
+        assertEquals(str,Vars.convert(null,bstr, "a","b"));
+
+        holder.setLength(0);
+        assertEquals(str = "%1%2",Vars.convert(holder,str, "a","b"));
+        assertEquals("ab",holder.toString());
+        assertEquals(str ,Vars.convert(null,str, "a","b"));
+        holder.setLength(0);
+        assertEquals(str,Vars.convert(holder,bstr="%s%s", "a","b"));
+        assertEquals("ab",holder.toString());
+        assertEquals(str ,Vars.convert(null,bstr, "a","b"));
+
+
+        holder.setLength(0);
+        assertEquals(str = " %1=%2 ",Vars.convert(holder,str, "a","b"));
+        assertEquals(" a=b ",holder.toString());
+        assertEquals(str ,Vars.convert(null,str, "a","b"));
+        holder.setLength(0);
+        assertEquals(str,Vars.convert(holder,bstr = " %s=%s ", "a","b"));
+        assertEquals(" a=b ",holder.toString());
+        assertEquals(str ,Vars.convert(null,bstr, "a","b"));
+
+        holder.setLength(0);
+        assertEquals(str = " %1%2%10 ",Vars.convert(holder,str, "a","b","c","d","e","f","g","h","i","j"));
+        assertEquals(" abj ",holder.toString());
+        assertEquals(str,Vars.convert(null,str, "a","b","c","d","e","f","g","h","i","j"));
+        holder.setLength(0);
+        assertEquals(str=" %1%2%3 ",Vars.convert(holder,bstr = " %s%s%s ", "a","b","c","d","e","f","g","h","i","j"));
+        assertEquals(" abc ",holder.toString());
+        assertEquals(str,Vars.convert(null,bstr, "a","b","c","d","e","f","g","h","i","j"));
+
+
+        holder.setLength(0);
+        assertEquals(str = "set %1 to %2",Vars.convert(holder,str, "Something much","larger"));
+        assertEquals("set Something much to larger",holder.toString());
+        assertEquals(str,Vars.convert(null,str,"Something much","larger"));
+        holder.setLength(0);
+        assertEquals(str,Vars.convert(holder,bstr="set %s to %s", "Something much","larger"));
+        assertEquals("set Something much to larger",holder.toString());
+        assertEquals(str,Vars.convert(null,bstr, "Something much","larger"));
+
+        holder.setLength(0);
+        assertEquals(str = "Text without Vars",Vars.convert(holder,str));
+        assertEquals(str,holder.toString());
+        assertEquals(str = "Text without Vars",Vars.convert(null,str));
+
+
+        holder.setLength(0);
+        assertEquals(str = "Not %1 Enough %2 Vars %3",Vars.convert(holder,str, "a","b"));
+        assertEquals("Not a Enough b Vars ",holder.toString());
+        assertEquals(str ,Vars.convert(null,str, "a","b"));
+        holder.setLength(0);
+        assertEquals(str,Vars.convert(holder,bstr="Not %s Enough %s Vars %s", "a","b"));
+        assertEquals("Not a Enough b Vars ",holder.toString());
+        assertEquals(str ,Vars.convert(null,bstr, "a","b"));
+
+        holder.setLength(0);
+        assertEquals(str = "!@#$%^*()-+?/,:;.",Vars.convert(holder,str, "a","b"));
+        assertEquals(str,holder.toString());
+        assertEquals(str ,Vars.convert(null,str, "a","b"));
+
+        holder.setLength(0);
+        bstr = "%s !@#$%^*()-+?/,:;.";
+        str = "%1 !@#$%^*()-+?/,:;.";
+        assertEquals(str,Vars.convert(holder,bstr, "Not Acceptable"));
+        assertEquals("Not Acceptable !@#$%^*()-+?/,:;.",holder.toString());
+        assertEquals(str ,Vars.convert(null,bstr, "Not Acceptable"));
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/wsse/test/JU_WSSEParser.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/wsse/test/JU_WSSEParser.java
new file mode 100644 (file)
index 0000000..c56cc1a
--- /dev/null
@@ -0,0 +1,164 @@
+/*******************************************************************************
+* ============LICENSE_START====================================================
+* * org.onap.ccsdk.apps
+* * ===========================================================================
+* * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+* * ===========================================================================
+* * Licensed under the Apache License, Version 2.0 (the "License");
+* * you may not use this file except in compliance with the License.
+* * You may obtain a copy of the License at
+* *
+*  *      http://www.apache.org/licenses/LICENSE-2.0
+* *
+*  * Unless required by applicable law or agreed to in writing, software
+* * distributed under the License is distributed on an "AS IS" BASIS,
+* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* * See the License for the specific language governing permissions and
+* * limitations under the License.
+* * ============LICENSE_END====================================================
+* *
+* *
+******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.wsse.test;
+
+import org.junit.Test;
+import org.onap.ccsdk.apps.cadi.wsse.WSSEParser;
+
+public class JU_WSSEParser {
+
+    @Test
+    public void test() {
+        @SuppressWarnings("unused")
+        WSSEParser wp = new WSSEParser();
+
+        // TODO: test the rest of this class
+//        final BasicCred bc = new BasicCred() {
+//            private String user;
+//            private byte[] password;
+//
+//            public void setUser(String user) { this.user = user; }
+//            public void setCred(byte[] passwd) { this.password = passwd; }
+//            public String getUser() { return user; }
+//            public byte[] getCred() { return password; }
+//        };
+
+//        FileInputStream fis;
+//        fis = new FileInputStream("test/example.xml");
+//        BufferedServletInputStream is = new BufferedServletInputStream(fis);
+//        try {
+//            is.mark(1536);
+//            try {
+//                assertNull(wp.parse(bc, is));
+//            } finally {
+//                is.reset();
+//                assertEquals(814,is.buffered());
+//            }
+//            String password = new String(bc.getCred());
+//            System.out.println("CadiWrap credentials are: " + bc.getUser() + ", " + password);
+//            assertEquals("some_user", bc.getUser());
+//            assertEquals("some_password", password);
+//
+//        } finally {
+//            fis.close();
+//        }
+//
+//        // CBUS (larger)
+//        fis = new FileInputStream("test/CBUSevent.xml");
+//        is = new BufferedServletInputStream(fis);
+//        try {
+//            is.mark(1536);
+//            try {
+//                assertNull(wp.parse(bc, is));
+//            } finally {
+//                is.reset();
+//                assertEquals(667,is.buffered());
+//            }
+//            String password = new String(bc.getCred());
+//            System.out.println("CadiWrap credentials are: " + bc.getUser() + ", " + password);
+//            assertEquals("none", bc.getUser());
+//            assertEquals("none", password);
+//
+//        } finally {
+//            fis.close();
+//        }
+//
+//        // Closed Stream
+//        fis = new FileInputStream("test/example.xml");
+//        fis.close();
+//        bc.setCred(null);
+//        bc.setUser(null);
+//        XMLStreamException ex = wp.parse(bc, fis);
+//        assertNotNull(ex);
+//        assertNull(bc.getUser());
+//        assertNull(bc.getCred());
+//
+//
+//        fis = new FileInputStream("test/exampleNoSecurity.xml");
+//        try {
+//            bc.setCred(null);
+//            bc.setUser(null);
+//            assertNull(wp.parse(bc, fis));
+//            assertNull(bc.getUser());
+//            assertNull(bc.getCred());
+//        } finally {
+//            fis.close();
+//        }
+//
+//        fis = new FileInputStream("test/exampleBad1.xml");
+//        try {
+//            bc.setCred(null);
+//            bc.setUser(null);
+//            assertNull(wp.parse(bc, fis));
+//            assertNull(bc.getUser());
+//            assertNull(bc.getCred());
+//        } finally {
+//            fis.close();
+//        }
+//
+//        XMLStreamException e = wp.parse(bc, new ByteArrayInputStream("Not XML".getBytes())); // empty
+//        assertNotNull(e);
+//
+//        e = wp.parse(bc, new ByteArrayInputStream("".getBytes())); // empty
+//        assertNotNull(e);
+//
+//
+//        long start, count = 0L;
+//        int iter = 30000;
+//        File f = new File("test/CBUSevent.xml");
+//        fis = new FileInputStream(f);
+//        is = new BufferedServletInputStream(fis);
+//        is.mark(0);
+//        try {
+//            while (is.read()>=0);
+//        } finally {
+//            fis.close();
+//        }
+//
+//        for (int i=0;i<iter;++i) {
+//            start = System.nanoTime();
+//            is.reset();
+//            try {
+//                assertNull(wp.parse(bc, is));
+//            } finally {
+//                count += System.nanoTime()-start;
+//            }
+//        }
+//        float ms = count/1000000f;
+//        System.out.println("Executed " + iter + " WSSE reads from Memory Stream in " + ms + "ms.  " + ms/iter + "ms per trans");
+//
+//        // SPECIFIC ISSUES
+//
+//        fis = new FileInputStream("test/error2013_04_23.xml");
+//        try {
+//            bc.setCred(null);
+//            bc.setUser(null);
+//            assertNull(wp.parse(bc, fis));
+//            assertNull(bc.getUser());
+//            assertNull(bc.getCred());
+//        } finally {
+//            fis.close();
+//        }
+    }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/wsse/test/JU_XReader.java b/cadi/core/src/test/java/org/onap/ccsdk/apps/cadi/wsse/test/JU_XReader.java
new file mode 100644 (file)
index 0000000..3cb7317
--- /dev/null
@@ -0,0 +1,153 @@
+/*******************************************************************************
+ * ============LICENSE_START====================================================
+ * * org.onap.ccsdk
+ * * ===========================================================================
+ * * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ *  *      http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ *  * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ * * ============LICENSE_END====================================================
+ * *
+ * *
+ ******************************************************************************/
+
+package org.onap.ccsdk.apps.cadi.wsse.test;
+
+import static org.junit.Assert.assertThat;
+import static org.hamcrest.CoreMatchers.is;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.events.XMLEvent;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.ccsdk.apps.cadi.wsse.XEvent;
+import org.onap.ccsdk.apps.cadi.wsse.XReader;
+
+public class JU_XReader {
+
+    private final static String TEST_DIR_NAME = "src/test/resources";
+    private final static String TEST_XML_NAME = "test.xml";
+    private static File testXML;
+
+    private final static String COMMENT = "a comment";
+    private final static String OUTER_TAG = "outerTag";
+    private final static String INNER_TAG = "innerTag";
+    private final static String DATA_TAG = "dataTag";
+    private final static String DATA = "some text that represents data";
+    private final static String SELF_CLOSING_TAG = "selfClosingTag";
+    private final static String PREFIX = "prefix";
+    private final static String SUFFIX = "suffix";
+
+    @BeforeClass
+    public static void setupOnce() throws IOException {
+        testXML = setupXMLFile();
+    }
+
+    @AfterClass
+    public static void tearDownOnce() {
+        testXML.delete();
+    }
+
+    @Test
+    public void test() throws XMLStreamException, IOException {
+        FileInputStream fis = new FileInputStream(TEST_DIR_NAME + '/' + TEST_XML_NAME);
+        try {
+            XReader xr = new XReader(fis);
+            assertThat(xr.hasNext(), is(true));
+            XEvent xe;
+
+            xe = getNextEvent(xr);
+            assertThat(xe.getEventType(), is(XMLEvent.START_DOCUMENT));
+
+            xe = getNextEvent(xr);
+            assertThat(xe.getEventType(), is(XMLEvent.START_ELEMENT));
+
+            xe = getNextEvent(xr);
+            assertThat(xe.getEventType(), is(XMLEvent.COMMENT));
+            assertThat(((XEvent.Comment)xe).value, is(COMMENT));
+
+            xe = getNextEvent(xr);
+            assertThat(xe.getEventType(), is(XMLEvent.START_ELEMENT));
+            assertThat(xe.asStartElement().getName().toString(), is(OUTER_TAG));
+
+            xe = getNextEvent(xr);
+            assertThat(xe.getEventType(), is(XMLEvent.START_ELEMENT));
+            assertThat(xe.asStartElement().getName().toString(), is(INNER_TAG));
+
+            xe = getNextEvent(xr);
+            assertThat(xe.getEventType(), is(XMLEvent.START_ELEMENT));
+            assertThat(xe.asStartElement().getName().toString(), is(DATA_TAG));
+
+            xe = getNextEvent(xr);
+            assertThat(xe.getEventType(), is(XMLEvent.CHARACTERS));
+            assertThat(xe.asCharacters().getData().toString(), is(DATA));
+
+            xe = getNextEvent(xr);
+            assertThat(xe.getEventType(), is(XMLEvent.END_ELEMENT));
+            assertThat(xe.asEndElement().getName().toString(), is(DATA_TAG));
+
+            xe = getNextEvent(xr);
+            assertThat(xe.getEventType(), is(XMLEvent.START_ELEMENT));
+            assertThat(xe.asStartElement().getName().toString(), is(SELF_CLOSING_TAG));
+
+            xe = getNextEvent(xr);
+            assertThat(xe.getEventType(), is(XMLEvent.START_ELEMENT));
+            assertThat(xe.asStartElement().getName().toString(), is(SUFFIX));
+
+            xe = getNextEvent(xr);
+            assertThat(xe.getEventType(), is(XMLEvent.END_ELEMENT));
+            assertThat(xe.asEndElement().getName().toString(), is(INNER_TAG));
+
+            xe = getNextEvent(xr);
+            assertThat(xe.getEventType(), is(XMLEvent.END_ELEMENT));
+            assertThat(xe.asEndElement().getName().toString(), is(OUTER_TAG));
+
+            assertThat(xr.hasNext(), is(false));
+
+        } finally {
+            fis.close();
+        }
+    }
+
+    private static XEvent getNextEvent(XReader xr) throws XMLStreamException {
+        if (xr.hasNext()) {
+            return xr.nextEvent();
+        }
+        return null;
+    }
+
+    private static File setupXMLFile() throws IOException {
+        File xmlFile = new File(TEST_DIR_NAME, TEST_XML_NAME);
+        PrintWriter writer = new PrintWriter(xmlFile);
+        writer.println("    ");  // Whitespace before the document - this is for coverage
+        writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+        writer.println("<!DOCTYPE xml>");
+        writer.println("<!--" + COMMENT + "-->");
+        writer.println("<" + OUTER_TAG + ">");
+        writer.println("  <" + INNER_TAG + ">");
+        writer.println("    <" + DATA_TAG + ">" + DATA + "</" + DATA_TAG + ">");
+        writer.println("    <" + SELF_CLOSING_TAG + " withAnAttribute=\"That has nested \\\" marks\" />");
+        writer.println("    <" + PREFIX + ":" + SUFFIX + "/>");
+        writer.println("  </" + INNER_TAG + ">");
+        writer.println("</" + OUTER_TAG + ">");
+        writer.flush();
+        writer.close();
+        return xmlFile;
+    }
+}
diff --git a/cadi/core/src/test/resources/AESKeyFile b/cadi/core/src/test/resources/AESKeyFile
new file mode 100644 (file)
index 0000000..35795c3
--- /dev/null
@@ -0,0 +1,27 @@
+xteeS5pA6m4SW2fMANEeF__VDm3F2wlUqyeUKDKxlSFiS_ICs0Eg7Xeqj3WqbgRqOisc1hLIbyk3
+2bal9qYwT59VxcZy-vrS3ytf0uu5gWwxGfo2-ut3CQTBfwVOj88RMdiyM13-dxJGOdQxT9_Czc9A
+it4edvcVQOTeazJ9JJ0KtO5tvsdihsaYYOVbMbMWPTzyDKY2KE7iMmPaqeGPLvxZSVvjQzjU8qMp
+OwzllAhRXZd0DWOullSotpt8P2VKcbnoKVA2SQvLTt5Zd9TziaaCMP88-fJQUhXvWhUPG_ZdH2R1
+MVyS0WrnBN6rY2h_aTiUswYZ6GGTDa_7O4AQixNR02NAbn7718Mw3bbe12d6nJZ2uYqMb9Hl1bzO
+-mZbJ_TUVAIUBgOb7XjScIS12JLlUuf-kIlQjfT2kfAzSuwcYHUZmB_jAfdZBjyhqVj4x7N47wb1
+7GbBBbECLAPMk9633_3HzadqZu6J3TmfmW2IYR9kqEF1NwfaXgJAL4I43YDSo2XyD-i9MUb3diYd
+LVElQP8gwMh2gbfRe_7BU49_HdbCk4n6BNgT0Z0EgtnMAA0ZZWmBTJTz5BlC0lXL-7NAWyOw1vRs
+ovjqc46zpQq8LYtJ2Vg5WwfpqBpyXqCdp9QYTNtN0GVB4iPBvaWRsQoZKzEESHavxKbGX2_Z7h2Q
+k03Okhl4Ud3MduR6pyxfxVqAdFu3xr2tEIcv_FjyD-5XiTfKcWPw-Srwy-_YiTy_io4nu2swC8Xm
+TsNcWtebM0W80L1nw0MwHFFIoAMBrHUjHxIrZL5JWZyaGxUdtnbKKlkVR1kDC8_pHrevwIijAEyi
+NnwDYaMw7tZo6f06J3yPVCVzVLLXFCCTkvdJAFBhaZI600mf7UZP2BMqomYVROoQNZDAO_GzP1sX
+_B4oebfYPkLk3fBkHasHPDZNy-oNHDEw8ytMXlMhyKX7UHUy28E8zpZWoRMmGPnzOSwp3P-Q08DP
+ja05l6vgvGtzuWNUKcFjSTdqx73JJJ1-QrZZlTd0N1gYqhRyh8YssGDYXEHh5zKuF6vNTinJwGLY
+P5NnOSBCbm9rcPcZGtZYer-uNUY9Z_rscfxiVork1SfnG25FwxCRkc_Nf47THAVM9T7m9Ou_g2N8
+eethvrQxDxEi_DVRBTJYe_9iUOec4KQY7VGhTiFbfvDPRB8yF7Znu5UPJXIeOjvcf9gi8lSwTTRx
+sqRpB3D5SJUSnBGzOCUvYRBbGP0olaVYyVXLcDknRTKbwkIf0tKAEFRDkvkXdlJnQ4lldFHuuHO9
+G7_iqEjCCNncdtLZMwe6LPe1usfJmnl3x95wkpVQdAKl7QoP5fMR2XoXwQbSO2qwIdgBwq9Zm6FW
+wRPStR0pS2ICjHusgmLPsdf2pVZ8q0fqjjzF4Ch7MfOWjhRsK9fCvVDXlrEOACTt7o0roXuswxKT
+EEbibkLsEAQOfOCYa66G37yQKRNnR8PWeRLAaZGF8ewfqF0c2KBAQuLYFlE8OthP_vFDKfVT2zMX
+BfneXOJNY2kZTEJA4MQOC4_Y1JJ7NJc1zqJRuPD8Ifo4oE1Qo2FE-mjm2G_Zb4XsmBEdWsiSAYum
+2DiGm5Io7OXQXv2zOKsBvcoG-24A4M3kTxhEH16sueTKY_DqOjjxkcVIUX_PM7TGkeRU9cnJ2sDH
+Y749blu8BWrRKSRiksxwwNAGW_IdElVVGd89gyGGRzZ2I4h-FXf9EBS1sqo-F_hOq_O3KOoMDFWK
+gdc4XIqeqmjwVTSpkKyxSCFYQW-aPBuTSdJYmPZRQQlCXwkm7SHbLRBKM4h62koA8A3hpzda3qnZ
+w_Wyb8u42yZpqNuUjUUOb4JApmOVCXIe4P9yfhiTbYRvGX50XjPIHBKjAzXhKLGaBaugBYhaGpXf
+kFjvsEF-4PrtQWORKudvlk8D7DnhxqgJdG0GoZAETBTCq_m1trg2TJ2WyAMidFUOWrgGPpshFq1F
+Nu7buFG6nsOsg4sfLmSm2oYhVb0TmEbBGRr_Apkg6nVJzX7DE_Rvt2slZDoIrXeKSbIJ_i5Y
\ No newline at end of file
diff --git a/cadi/core/src/test/resources/CBUSevent.xml b/cadi/core/src/test/resources/CBUSevent.xml
new file mode 100644 (file)
index 0000000..d0f7e34
--- /dev/null
@@ -0,0 +1,44 @@
+<!--
+  ============LICENSE_START====================================================
+  * org.onap.ccsdk
+  * ===========================================================================
+  * Copyright Â© 2023 AT&T Intellectual Property. All rights reserved.
+  * ===========================================================================
+  * Licensed under the Apache License, Version 2.0 (the "License");
+  * you may not use this file except in compliance with the License.
+  * You may obtain a copy of the License at
+  * 
+   *      http://www.apache.org/licenses/LICENSE-2.0
+  * 
+   * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  * ============LICENSE_END====================================================
+  *
+-->
+<assembly>
+  <id>app</id>
+  <formats>
+    <format>jar</format>
+  </formats>
+  <includeBaseDirectory>false</includeBaseDirectory>
+  <dependencySets>
+    <dependencySet>
+      <outputDirectory></outputDirectory>
+      <outputFileNameMapping></outputFileNameMapping>
+      <unpack>true</unpack>
+      <scope>runtime</scope>
+      <!--  includes>
+        <include>web</include>
+      </includes -->
+    </dependencySet>
+  </dependencySets>
+  <fileSets>
+    <fileSet>
+      <directory>target/classes</directory>
+      <outputDirectory></outputDirectory>
+    </fileSet>
+   </fileSets>
+</assembly>
diff --git a/cadi/core/src/test/resources/keyfile b/cadi/core/src/test/resources/keyfile
new file mode 100644 (file)
index 0000000..e84bd61
--- /dev/null
@@ -0,0 +1,27 @@
+9zgJxUXT1CrzC_A2Z0PdKi3n9l6zmErB26ZlSXCCyloxi3bGqD3lNHC3aFHfgC8-ZwNMuLBM93WY
+JV4sEacNodHGjgmAqSVyMHiPTEP4XRrydfjXAvaBIERcU1Yvu4pa4Mq25RXLHt8tIAnToFVbq82n
+bjkfdcv2-shgwkEvRiNIdK5TITO8JTvTRWND5MqXc9gnCKkR6Rl5dU5QGIB2SxWOPCvKBBWeUGRO
+bSinrjkI-iXabuLOYUaGo6FI_XAU5S9WxvfrDVpBijUAGJW8QZe1oBIo5QmQlx6ONB4ohjEu89ZZ
+gTee22MvSNUvaT8IGbj_Zt_TyuCqcdmkVahWp5ffeK2J3bmHActAC2IxXD4yV-sFLB7PW7I8KMA7
+tML3Lcy9ozmYa2E8N8B9uQ0zMHz_TVpPvj5xkVF4_FEKOTD1mkf-JYC1CyzwJS2YWWxO6fqsxIjD
+1qB4OJudv4RK6hSxdVrNxc_wchVAGXVD6ulm8UPBGP_wpfItP8BGYwCHlOjUrZofewKB2Aa9Uk9m
+oyk309WmPVBeRzZ0vRlXUp8jhKlAPISvv8CBbG-6SuXAszY2qedgd3huYKNreVN-xMZM2hnYbEUW
+0sdcqpFqIV039Awfwjn5sZPFW4iT3yWhxib1PwFzwfaXnrwgwbLAda68mRDAWCrsDRu11IiQJqb7
+cjNLYBOGDVhX7jeUyBJUzW-xhl__DsoCZSqP39vFoPtglXHlQNtVqQ8d96mu_QMY5bcuhevI4RQ_
+SD7WcRyAiUztiC4Eb6BYwld0RITdB1-Y43jkZlfA8Ej5Zw8sX_-2J2hKdDPT4KrTYWA5T6wiIJK9
+lxIc39wGHpxQ4kz8gx0VeqRU2hgHVKovuaEvBnwv8JW3qeuowaUmiPi7UuIRwi4pFX5iQv62yrfO
+5Z6EXBDVI8Ikq4UTu70vX_bCuXHtvqm97PFh2KXjBHS--iNVQ5GhnDKKv_Fd4naQjCSwTTgtxD4X
+ASgLSSETGJ8wAjWHOWUuVT4jUDFIQwunNaH6y2NaDWA0tkO74oYaQIL_-kd9ChGLzGL389v8BV2X
+oaw70W9L3-OOtzAz-hACbOtbbMkx2bVMmS8QhjYg-_2bpwSb8NR322pQ9AodFTU4x5HrLoERk2Rw
+hRExZP7K-_idMJUGLF9gJFFS01UyBLijyWGyN0teQleXgn6IzZk7dH9roddoe9IacjiV7XfE4i1U
+rVNTRKiDdHSX02KGOihs_j-Tf0PYsz0wEeACINA5MafGzc9x2b8yMzBxwPHxRszjL4dymCoLXRI5
+srLsWk2Jwtp9meW8jhkoAi5xUKzLiYIhEohIX3eEEA0O0wuK0fzcMB7IbyTYYazawUKmUXZ94OLu
+Fmb-UaAEvU-9U4O3DNfbDN2ELxUHmWaqNqpGl1IV0ZxGrKNZi9Rga9-_vfVGcoVMD7vZOhiZddc9
+WRlom3tQZRx2Sm42baNH8wS34J0KuUYPcjQ-1_GEJxcH0hv6hzSm4is7mUdnyB95g1UohKdQOfaY
+tOdHlXbu2zG6SyPaYyQFfQbMPwBn-hx_7bYj9Px-EhYeMpBIP8X98jkd3BlWY4sdWqxsQfAb5pml
+cnDRynHag2XxLqttAWSwru_owfeXzmYsPD-PINRu-Csjzlbdhq73amTFN-U8mYA09dlCck2fW8qo
+mAXLkVlboVaPuem6WvfSd93ZinsB5Wi5RX6RQxeHeo88cWrJ11Au14J8xFlurcZwdSjO4dsnZj_D
+ry0uKWsyNoLogBuDansiNGGO8-1qsyRxVp3zbxOMQmPouN6l0ZfxQdACqX8_4HTD7NMNMnLYjPjC
+4YfOUx4pQMdjzno05vuF5zY-UQ3SN7HkmXsF6tVJdt15cmtLFetD5LTbvdRr1eeHWuwD4-aJQx4T
+SdOLQ3zHeMnNFsxR_xKsu4AGjcC2-TpGixmA1kJtYBm1WIGoxQ6N4rneEo-82yvKwYst9-DJcV6x
+xy1dpJqtx3I7M6DqPVURomeh2czO6UMRPVIQ1ltj4E27_FWFsWC38ZyR4nFimovFLJNCzy2k
\ No newline at end of file
diff --git a/cadi/pom.xml b/cadi/pom.xml
new file mode 100755 (executable)
index 0000000..f26befa
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onap.ccsdk.apps</groupId>
+           <artifactId>ccsdk-apps</artifactId>
+        <version>1.6.0-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.onap.ccsdk.apps</groupId>
+    <artifactId>ccsdk-apps-cadi</artifactId>
+    <version>1.6.0-SNAPSHOT</version>
+    <packaging>pom</packaging>
+
+    <name>ccsdk-apps-cadi</name>
+    <description>CCSDK port of AAF CADI</description>
+    <url>https://wiki.onap.org</url>
+    <organization>
+        <name>ONAP</name>
+    </organization>
+
+    <modules>
+        <module>core</module>
+    </modules>
+
+</project>
index 7b3d3f0..36bb45c 100644 (file)
         <swagger.directory>${basedir}/target/classes/META-INF/resources/swagger</swagger.directory>
         <icd.file>service.json</icd.file>
         <icd.package>org.onap.ccsdk.apps.ms.neng.service.rs</icd.package>
-        <java.version>17</java.version>
+        <java.version>11</java.version>
         <docker.registry>nexus3.onap.org:10001</docker.registry>
         <build.number>local</build.number>
         <kube.namespace>TBD</kube.namespace>
         <service.account>TBD</service.account>
         <namespace>org.onap.ccsdk.apps.ms.neng</namespace>
-        <maven.compiler.source>17</maven.compiler.source>
-        <maven.compiler.target>17</maven.compiler.target>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <sdk.java.rest>6.2.0.11</sdk.java.rest>
         <serviceArtifactName>ms-networkelementnamegen</serviceArtifactName>
             <scope>compile</scope>
         </dependency>
         <dependency>
-            <groupId>org.apache.httpcomponents</groupId>
-            <artifactId>httpclient</artifactId>
+            <groupId>org.apache.httpcomponents.client5</groupId>
+            <artifactId>httpclient5</artifactId>
         </dependency>
         <dependency>
             <groupId>javax.ws.rs</groupId>
index ca918eb..c766b16 100644 (file)
@@ -26,7 +26,7 @@ import org.springframework.boot.web.client.RestTemplateBuilder;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
 /**
  * Configuration for the API part of the micro-service.
@@ -37,8 +37,8 @@ public class WebConfiguration {
      * Creates the bean for configuring swagger.
      */
     @Bean
-    public WebMvcConfigurerAdapter forwardToIndex() {
-        return new WebMvcConfigurerAdapter() {
+    public WebMvcConfigurer forwardToIndex() {
+        return new WebMvcConfigurer() {
             @Override
             public void addViewControllers(ViewControllerRegistry registry) {
                 registry.addViewController("/swagger").setViewName("redirect:/swagger/index.html");
index 3772fd7..14ed9be 100644 (file)
@@ -30,7 +30,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.logging.Logger;
 import java.util.regex.Pattern;
-import javax.transaction.Transactional;
+import jakarta.transaction.Transactional;
 import org.onap.ccsdk.apps.ms.neng.core.exceptions.NengException;
 import org.onap.ccsdk.apps.ms.neng.core.gen.NameGenerator;
 import org.onap.ccsdk.apps.ms.neng.core.persistence.NamePersister;
index 7a729af..40343a3 100644 (file)
@@ -22,10 +22,10 @@ package org.onap.ccsdk.apps.ms.neng.persistence.entity;
 
 import java.io.Serializable;
 import java.sql.Timestamp;
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.Id;
-import javax.persistence.Table;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
 
 /**
  * Entity representing the parameters and configuration of an external system/sub-system/application interface
index b5a7463..5831f42 100644 (file)
@@ -22,12 +22,12 @@ package org.onap.ccsdk.apps.ms.neng.persistence.entity;
 
 import java.io.Serializable;
 import java.sql.Timestamp;
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.Table;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
 
 /**
  * Represents a generated name.
index de8bef1..113af25 100644 (file)
@@ -22,10 +22,10 @@ package org.onap.ccsdk.apps.ms.neng.persistence.entity;
 
 import java.io.Serializable;
 import java.sql.Timestamp;
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.Id;
-import javax.persistence.Table;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
 
 /**
  * Maps identifiers (such as function names) in the policy to internal names used by this
index 0141073..1e5df40 100644 (file)
@@ -22,12 +22,12 @@ package org.onap.ccsdk.apps.ms.neng.persistence.entity;
 
 import java.io.Serializable;
 import java.sql.Timestamp;
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.Table;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
 
 /**
  * Represents an entity representing policies stored in this micro-service (temporarily).
index 92cfa4f..c90d867 100644 (file)
@@ -22,10 +22,10 @@ package org.onap.ccsdk.apps.ms.neng.persistence.entity;
 
 import java.io.Serializable;
 import java.sql.Timestamp;
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.Id;
-import javax.persistence.Table;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
 
 /**
  * General parameters controlling this micro-service. 
index 41bfae4..2880f30 100644 (file)
@@ -101,7 +101,7 @@ public class AaiServiceImpl {
                 throw new NengException("Error while validating name with A&AI");
             }
         } catch (HttpClientErrorException e) {
-            log.warning(e.getStatusCode().name() + " -- " + e.getResponseBodyAsString());
+            log.warning(e.getStatusCode() + " -- " + e.getResponseBodyAsString());
             if (HttpStatus.NOT_FOUND.equals(e.getStatusCode())) {
                 return buildResponse(false);
             }
index 6ae3c20..d855eb4 100644 (file)
@@ -35,10 +35,13 @@ import java.util.logging.Logger;
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLSession;
-import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
-import org.apache.http.conn.ssl.TrustStrategy;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClients;
+
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
+import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
+import org.apache.hc.core5.ssl.TrustStrategy;
 import org.onap.ccsdk.apps.ms.neng.core.exceptions.NengException;
 import org.onap.ccsdk.apps.ms.neng.core.policy.PolicyFinder;
 import org.onap.ccsdk.apps.ms.neng.core.resource.model.GetConfigRequest;
@@ -231,13 +234,17 @@ public class PolicyFinderServiceImpl implements PolicyFinder {
         if (restTemplate != null) {
             return restTemplate;
         }
         TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
-        SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
+        SSLContext sslContext = org.apache.hc.core5.ssl.SSLContexts.custom()
                         .loadTrustMaterial(null, acceptingTrustStrategy).build();
         HostnameVerifier verifier = new AcceptIpAddressHostNameVerifier(disableHostVerification);
         SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, verifier);
-        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();
+
+        PoolingHttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create().setSSLSocketFactory(csf).build();
+        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();
         HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
+
         requestFactory.setHttpClient(httpClient);
         restTemplate = new RestTemplate(requestFactory);
         restTemplate.getInterceptors().add(getAuthInt());
index fe889f9..30071cb 100644 (file)
 
     <properties>
         <start-class>org.onap.ccsdk.apps.ms.sliboot.SlibootApp</start-class>
-        <aaf.cadi.version>2.1.21</aaf.cadi.version>
         <base.image.name>onap/ccsdk-alpine-j17-image</base.image.name>
-        <base.image.version>1.5-STAGING-latest</base.image.version>
+        <base.image.version>1.6-STAGING-latest</base.image.version>
         <image.name>onap/ccsdk-sliboot-alpine-image</image.name>
         <ccsdk.project.version>${project.version}</ccsdk.project.version>
-        <ccsdk.distribution.version>1.5.1</ccsdk.distribution.version>
+        <ccsdk.distribution.version>1.6.0-SNAPSHOT</ccsdk.distribution.version>
         <ccsdk.build.timestamp>${maven.build.timestamp}</ccsdk.build.timestamp>
         <maven.build.timestamp.format>yyyyMMdd'T'HHmmss'Z'</maven.build.timestamp.format>
         <ccsdk.sliboot.jar>${project.artifactId}-${project.version}-exec.jar</ccsdk.sliboot.jar>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
-            <exclusions>
-                <exclusion>
-                    <groupId>org.springframework.boot</groupId>
-                    <artifactId>spring-boot-starter-logging</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-log4j2</artifactId>
         </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-data-jpa</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.antlr</groupId>
+                    <artifactId>antlr4-runtime</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
         <dependency>
             <groupId>io.springfox</groupId>
             <version>${springfox.version}</version>
         </dependency>
         <dependency>
-            <groupId>org.onap.aaf.authz</groupId>
-            <artifactId>aaf-cadi-client</artifactId>
-            <version>${aaf.cadi.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.onap.aaf.authz</groupId>
-            <artifactId>aaf-cadi-core</artifactId>
-            <version>${aaf.cadi.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.onap.aaf.authz</groupId>
-            <artifactId>aaf-auth-client</artifactId>
-            <version>${aaf.cadi.version}</version>
-            <scope>runtime</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.onap.aaf.authz</groupId>
-            <artifactId>aaf-misc-env</artifactId>
-            <version>${aaf.cadi.version}</version>
-            <scope>runtime</scope>
-            <exclusions>
-                <exclusion>
-                    <groupId>log4j</groupId>
-                    <artifactId>log4j</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-        <dependency>
-            <groupId>org.onap.aaf.authz</groupId>
-            <artifactId>aaf-misc-rosetta</artifactId>
-            <version>${aaf.cadi.version}</version>
-            <scope>runtime</scope>
+            <groupId>org.onap.ccsdk.apps</groupId>
+            <artifactId>ccsdk-apps-cadi-core</artifactId>
+            <version>${project.version}</version>
         </dependency>
         <!-- Needed by logging-analytics payload logging filter -->
         <dependency>
             <groupId>org.apache.cxf</groupId>
             <artifactId>cxf-spring-boot-starter-jaxrs</artifactId>
-            <version>3.4.4</version>
-        </dependency>
-        <dependency>
-            <groupId>org.onap.aaf.authz</groupId>
-            <artifactId>aaf-cadi-client</artifactId>
-            <version>${aaf.cadi.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.onap.aaf.authz</groupId>
-            <artifactId>aaf-cadi-core</artifactId>
-            <version>${aaf.cadi.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.onap.aaf.authz</groupId>
-            <artifactId>aaf-auth-client</artifactId>
-            <version>${aaf.cadi.version}</version>
-            <scope>runtime</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.onap.aaf.authz</groupId>
-            <artifactId>aaf-misc-env</artifactId>
-            <version>${aaf.cadi.version}</version>
-            <scope>runtime</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.onap.aaf.authz</groupId>
-            <artifactId>aaf-misc-rosetta</artifactId>
-            <version>${aaf.cadi.version}</version>
-            <scope>runtime</scope>
+            <version>4.0.2</version>
         </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>derby</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.derby</groupId>
+            <artifactId>derbytools</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.antlr</groupId>
+            <artifactId>antlr4-runtime</artifactId>
+            <version>${antlr4.version}</version>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.antlr</groupId>
+            <artifactId>antlr4</artifactId>
+            <version>${antlr4.version}</version>
+            <scope>runtime</scope>
+        </dependency>
         <dependency>
             <groupId>org.mariadb.jdbc</groupId>
             <artifactId>mariadb-java-client</artifactId>
             <groupId>org.glassfish.jersey.inject</groupId>
             <artifactId>jersey-hk2</artifactId>
         </dependency>
+
     </dependencies>
 
     <build>
             <plugin>
                 <groupId>io.swagger</groupId>
                 <artifactId>swagger-codegen-maven-plugin</artifactId>
-                <version>2.3.1</version>
+                <version>2.4.32</version>
                 <executions>
                     <execution>
                         <phase>generate-sources</phase>
                             <withXml>true</withXml>
                             <templateDirectory>${project.basedir}/src/main/templates</templateDirectory>
                             <configOptions>
-                                <java8>true</java8>
-                                <springBootVersion>2.2.4-RELEASE</springBootVersion>
+                                <java11>true</java11>
+                                <springBootVersion>3.0.3</springBootVersion>
+                                <jakarta>true</jakarta>
+                                <dateLibrary>java11</dateLibrary>
                             </configOptions>
                         </configuration>
                     </execution>
index 295953a..ebacc04 100644 (file)
@@ -5,7 +5,7 @@ import java.io.FileReader;
 import java.io.IOException;
 import java.util.Properties;
 
-import org.onap.aaf.cadi.filter.CadiFilter;
+import org.onap.ccsdk.apps.cadi.filter.CadiFilter;
 import org.onap.ccsdk.sli.core.utils.common.EnvProperties;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
index 0fd23a9..e393dd0 100644 (file)
@@ -35,7 +35,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
 import springfox.documentation.swagger2.annotations.EnableSwagger2;\r
 import org.springframework.context.annotation.Bean;\r
 \r
-import org.onap.aaf.cadi.filter.CadiFilter;\r
+import org.onap.ccsdk.apps.cadi.filter.CadiFilter;\r
 \r
 @SpringBootApplication(scanBasePackages={ "org.onap.ccsdk.apps.ms.sliboot.*", "org.onap.ccsdk.apps.services" })\r
 @EnableJpaRepositories("org.onap.ccsdk.apps.ms.sliboot.*")\r
index 4498184..2383fe6 100644 (file)
@@ -47,8 +47,8 @@ import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.RestController;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.validation.Valid;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.validation.Valid;
 import java.util.*;
 
 @javax.annotation.Generated(value = "io.swagger.codegen.languages.SpringCodegen", date = "2020-02-20T12:50:11.207-05:00")
index 1ca4fe1..8081d6a 100644 (file)
@@ -1,6 +1,6 @@
 package org.onap.ccsdk.apps.ms.sliboot.data;
 
-import javax.persistence.*;
+import jakarta.persistence.*;
 
 @Entity(name = "TEST_RESULT_CONFIG")
 @Table(name = "TEST_RESULT_CONFIG")
index b9d0993..ab3aedb 100644 (file)
@@ -1,6 +1,6 @@
 package org.onap.ccsdk.apps.ms.sliboot.data;
 
-import javax.persistence.*;
+import jakarta.persistence.*;
 
 @Entity(name="TEST_RESULT_OPERATIONAL")
 @Table(name="TEST_RESULT_OPERATIONAL")
index c28642c..91483ed 100644 (file)
@@ -7,15 +7,11 @@ package {{package}};
 
 {{#imports}}import {{import}};
 {{/imports}}
-{{#jdk8-no-delegate}}
-    import com.fasterxml.jackson.databind.ObjectMapper;
-{{/jdk8-no-delegate}}
+import com.fasterxml.jackson.databind.ObjectMapper;
 import io.swagger.annotations.*;
-{{#jdk8-no-delegate}}
-    import org.slf4j.Logger;
-    import org.slf4j.LoggerFactory;
-    import org.springframework.http.HttpStatus;
-{{/jdk8-no-delegate}}
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 {{#useBeanValidation}}
     import org.springframework.validation.annotation.Validated;
@@ -30,33 +26,37 @@ import org.springframework.web.bind.annotation.RequestPart;
 import org.springframework.web.multipart.MultipartFile;
 import org.onap.ccsdk.apps.services.RestException;
 
-{{#jdk8-no-delegate}}
-    import javax.servlet.http.HttpServletRequest;
-{{/jdk8-no-delegate}}
+
+{{#jakarta}}
+import jakarta.servlet.*;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+{{/jakarta}}
+{{^jakarta}}
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+{{/jakarta}}
 {{#useBeanValidation}}
-    import javax.validation.Valid;
-    import javax.validation.constraints.*;
+{{#jakarta}}
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.*;
+{{/jakarta}}
+{{^jakarta}}
+import javax.validation.Valid;
+import javax.validation.constraints.*;
+{{/jakarta}}
 {{/useBeanValidation}}
-{{#jdk8-no-delegate}}
-    import java.io.IOException;
-{{/jdk8-no-delegate}}
+import java.io.IOException;
 import java.util.List;
-{{#jdk8-no-delegate}}
-    import java.util.Optional;
-{{/jdk8-no-delegate}}
-{{^jdk8-no-delegate}}
-    {{#useOptional}}
-        import java.util.Optional;
-    {{/useOptional}}
-{{/jdk8-no-delegate}}
+import java.util.Optional;
 {{#async}}
-    import java.util.concurrent.{{^jdk8}}Callable{{/jdk8}}{{#jdk8}}CompletableFuture{{/jdk8}};
+import java.util.concurrent.{{^jdk8}}Callable{{/jdk8}}{{#jdk8}}CompletableFuture{{/jdk8}};
 {{/async}}
 {{>generatedAnnotation}}
 @Api(value = "{{{baseName}}}", description = "the {{{baseName}}} API")
 {{#operations}}
     public interface {{classname}} {
-    {{#jdk8}}
 
         {{^isDelegate}}
             Logger log = LoggerFactory.getLogger({{classname}}.class);
@@ -76,7 +76,6 @@ import java.util.List;
         {{#isDelegate}}
             {{classname}}Delegate getDelegate();
         {{/isDelegate}}
-    {{/jdk8}}
     {{#operation}}
 
             @ApiOperation(value = "{{{summary}}}", nickname = "{{{operationId}}}", notes = "{{{notes}}}"{{#returnBaseType}}, response = {{{returnBaseType}}}.class{{/returnBaseType}}{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}{{#hasAuthMethods}}, authorizations = {
@@ -101,7 +100,7 @@ import java.util.List;
                 produces = { {{#produces}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/produces}} }, {{/hasProduces}}{{#hasConsumes}}
                 consumes = { {{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} },{{/hasConsumes}}{{/singleContentTypes}}
             method = RequestMethod.{{httpMethod}})
-        {{#jdk8}}default {{/jdk8}}{{#responseWrapper}}{{.}}<{{/responseWrapper}}ResponseEntity<{{>returnTypes}}>{{#responseWrapper}}>{{/responseWrapper}} {{#delegate-method}}_{{/delegate-method}}{{operationId}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{#hasMore}},{{/hasMore}}{{/allParams}}){{^jdk8}};{{/jdk8}}{{#jdk8}} throws RestException {
+            default {{#responseWrapper}}{{.}}<{{/responseWrapper}}ResponseEntity<{{>returnTypes}}>{{#responseWrapper}}>{{/responseWrapper}} {{#delegate-method}}_{{/delegate-method}}{{operationId}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{#hasMore}},{{/hasMore}}{{/allParams}}) throws RestException {
         {{#delegate-method}}
                 return {{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});
                 }
@@ -129,7 +128,7 @@ import java.util.List;
         {{#isDelegate}}
                 return getDelegate().{{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});
         {{/isDelegate}}
-            }{{/jdk8}}
+            }
 
     {{/operation}}
         }
diff --git a/ms/sliboot/src/main/templates/apiOriginFilter.mustache b/ms/sliboot/src/main/templates/apiOriginFilter.mustache
new file mode 100644 (file)
index 0000000..3910a6b
--- /dev/null
@@ -0,0 +1,38 @@
+package {{apiPackage}};
+
+import java.io.IOException;
+
+{{#jakarta}}
+import jakarta.servlet.*;
+import jakarta.servlet.http.HttpServletResponse;
+{{/jakarta}}
+{{^jakarta}}
+import javax.servlet.*;
+import javax.servlet.http.HttpServletResponse;
+{{/jakarta}}
+
+{{>generatedAnnotation}}
+{{#jakarta}}
+public class ApiOriginFilter implements jakarta.servlet.Filter {
+{{/jakarta}}
+{{^jakarta}}
+public class ApiOriginFilter implements javax.servlet.Filter {
+{{/jakarta}}
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response,
+            FilterChain chain) throws IOException, ServletException {
+        HttpServletResponse res = (HttpServletResponse) response;
+        res.addHeader("Access-Control-Allow-Origin", "*");
+        res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
+        res.addHeader("Access-Control-Allow-Headers", "Content-Type");
+        chain.doFilter(request, response);
+    }
+
+    @Override
+    public void destroy() {
+    }
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+    }
+}
diff --git a/ms/sliboot/src/main/templates/libraries/spring-boot/swagger2SpringBoot.mustache b/ms/sliboot/src/main/templates/libraries/spring-boot/swagger2SpringBoot.mustache
new file mode 100644 (file)
index 0000000..5f1d0f0
--- /dev/null
@@ -0,0 +1,50 @@
+package {{basePackage}};
+
+import {{configPackage}}.LocalDateConverter;
+import {{configPackage}}.LocalDateTimeConverter;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.ExitCodeGenerator;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.ComponentScan;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.format.FormatterRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+@SpringBootApplication
+@EnableSwagger2
+@ComponentScan(basePackages = { "{{basePackage}}", "{{apiPackage}}" , "{{configPackage}}"})
+public class Swagger2SpringBoot implements CommandLineRunner {
+
+    @Override
+    public void run(String... arg0) throws Exception {
+        if (arg0.length > 0 && arg0[0].equals("exitcode")) {
+            throw new ExitException();
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        new SpringApplication(Swagger2SpringBoot.class).run(args);
+    }
+
+    @Configuration
+    static class MyConfig implements WebMvcConfigurer {
+        @Override
+        public void addFormatters(FormatterRegistry registry) {
+            registry.addConverter(new LocalDateConverter("{{#datePattern}}{{datePattern}}{{/datePattern}}{{^datePattern}}yyyy-MM-dd{{/datePattern}}"));
+            registry.addConverter(new LocalDateTimeConverter("{{#dateTimePattern}}{{dateTimePattern}}{{/dateTimePattern}}{{^dateTimePattern}}yyyy-MM-dd'T'HH:mm:ss.SSS{{/dateTimePattern}}"));
+        }
+    }
+
+    class ExitException extends RuntimeException implements ExitCodeGenerator {
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        public int getExitCode() {
+            return 10;
+        }
+
+    }
+}
diff --git a/ms/sliboot/src/main/templates/libraries/spring-mvc/swaggerUiConfiguration.mustache b/ms/sliboot/src/main/templates/libraries/spring-mvc/swaggerUiConfiguration.mustache
new file mode 100644 (file)
index 0000000..de7ccaf
--- /dev/null
@@ -0,0 +1,96 @@
+package {{configPackage}};
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+{{#threetenbp}}
+import com.fasterxml.jackson.datatype.threetenbp.ThreeTenModule;
+{{/threetenbp}}
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.Bean;
+import org.springframework.format.FormatterRegistry;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+{{#threetenbp}}
+import org.threeten.bp.Instant;
+import org.threeten.bp.OffsetDateTime;
+import org.threeten.bp.ZonedDateTime;
+{{/threetenbp}}
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+import java.util.List;
+
+{{>generatedAnnotation}}
+@Configuration
+@ComponentScan(basePackages = "{{apiPackage}}")
+@EnableWebMvc
+@EnableSwagger2 //Loads the spring beans required by the framework
+@PropertySource("classpath:swagger.properties")
+@Import(SwaggerDocumentationConfig.class)
+public class SwaggerUiConfiguration implements WebMvcConfigurer {
+  private static final String[] SERVLET_RESOURCE_LOCATIONS = { "/" };
+
+  private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
+      "classpath:/META-INF/resources/", "classpath:/resources/",
+      "classpath:/static/", "classpath:/public/" };
+
+  private static final String[] RESOURCE_LOCATIONS;
+  static {
+    RESOURCE_LOCATIONS = new String[CLASSPATH_RESOURCE_LOCATIONS.length
+        + SERVLET_RESOURCE_LOCATIONS.length];
+    System.arraycopy(SERVLET_RESOURCE_LOCATIONS, 0, RESOURCE_LOCATIONS, 0,
+        SERVLET_RESOURCE_LOCATIONS.length);
+    System.arraycopy(CLASSPATH_RESOURCE_LOCATIONS, 0, RESOURCE_LOCATIONS,
+        SERVLET_RESOURCE_LOCATIONS.length, CLASSPATH_RESOURCE_LOCATIONS.length);
+  }
+
+  private static final String[] STATIC_INDEX_HTML_RESOURCES;
+  static {
+    STATIC_INDEX_HTML_RESOURCES = new String[RESOURCE_LOCATIONS.length];
+    for (int i = 0; i < STATIC_INDEX_HTML_RESOURCES.length; i++) {
+      STATIC_INDEX_HTML_RESOURCES[i] = RESOURCE_LOCATIONS[i] + "index.html";
+    }
+  }
+
+  @Override
+  public void addResourceHandlers(ResourceHandlerRegistry registry) {
+    if (!registry.hasMappingForPattern("/webjars/**")) {
+      registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
+    }
+    if (!registry.hasMappingForPattern("/**")) {
+      registry.addResourceHandler("/**").addResourceLocations(RESOURCE_LOCATIONS);
+    }
+  }
+
+  @Bean
+  public Jackson2ObjectMapperBuilder builder() {
+    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
+        .indentOutput(true)
+        .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
+        .dateFormat(new RFC3339DateFormat());
+    return builder;
+  }
+
+  @Override
+  public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
+    converters.add(new MappingJackson2HttpMessageConverter(objectMapper()));
+    super.configureMessageConverters(converters);
+  }
+
+  @Override
+  public void addFormatters(FormatterRegistry registry) {
+    registry.addConverter(new LocalDateConverter("{{#datePattern}}{{datePattern}}{{/datePattern}}{{^datePattern}}yyyy-MM-dd{{/datePattern}}"));
+    registry.addConverter(new LocalDateTimeConverter("{{#dateTimePattern}}{{dateTimePattern}}{{/dateTimePattern}}{{^dateTimePattern}}yyyy-MM-dd'T'HH:mm:ss.SSS{{/dateTimePattern}}"));
+  }
+
+  @Bean
+  public ObjectMapper objectMapper(){
+    return builder().build();
+  }
+}
index 585ed5c..543b49a 100644 (file)
@@ -19,8 +19,8 @@
     <properties>
         <swagger.directory>${basedir}/target/main/resources/META-INF/resources/swagger-ui/dist</swagger.directory>
         <swagger.annotations.version>1.5.8</swagger.annotations.version>
-        <java.version>17</java.version>
-        <springboot.version>2.0.4.RELEASE</springboot.version>
+        <java.version>11</java.version>
+        <springboot.version>3.0.3</springboot.version>
         <mariadb.connector.version>2.1.1</mariadb.connector.version>
         <docker.registry>TBD:5100</docker.registry>
         <serviceArtifactName>vlantagapi</serviceArtifactName>
             <artifactId>spring-boot-starter-security</artifactId>
         </dependency>
 
+
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>org.onap.ccsdk.sli.core</groupId>
             <artifactId>sli-common</artifactId>
index 80c42fb..01860fa 100644 (file)
@@ -21,11 +21,11 @@ import java.util.List;
 import org.slf4j.Logger;\r
 import org.slf4j.LoggerFactory;\r
 import org.springframework.beans.factory.annotation.Autowired;\r
+import org.springframework.context.annotation.Bean;\r
 import org.springframework.context.annotation.Configuration;\r
 import org.springframework.core.env.Environment;\r
 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\r
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;\r
-import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\r
 import org.springframework.security.config.http.SessionCreationPolicy;\r
 import org.springframework.security.core.userdetails.User;\r
 import org.springframework.security.core.userdetails.UserDetails;\r
@@ -33,6 +33,7 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.security.crypto.factory.PasswordEncoderFactories;\r
 import org.springframework.security.crypto.password.PasswordEncoder;\r
 import org.springframework.security.provisioning.InMemoryUserDetailsManager;\r
+import org.springframework.security.web.SecurityFilterChain;\r
 \r
 /**\r
  * ApplicationSecurityConfig.java Purpose: Configures and validates\r
@@ -42,14 +43,14 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager;
  * @version 1.0\r
  */\r
 @Configuration\r
-public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter{\r
+public class ApplicationSecurityConfig {\r
        private Logger logger = LoggerFactory.getLogger(ApplicationSecurityConfig.class);\r
         \r
        @Autowired\r
        private Environment environment;\r
 \r
-       @Override\r
-       protected void configure(AuthenticationManagerBuilder auth) throws Exception {\r
+       @Bean\r
+       public InMemoryUserDetailsManager userDetailsService() {\r
                List<UserDetails> userDetails = new ArrayList<>();\r
                \r
                // Explicitly set bcrypt password encoder rather than using default\r
@@ -74,7 +75,7 @@ public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter{
                }\r
                \r
                logger.info("-------------------------------{}",userDetails);\r
-               auth.userDetailsService(inMemoryUserDetailsManager(userDetails));\r
+               return new InMemoryUserDetailsManager(userDetails);\r
        }\r
        \r
     \r
@@ -82,13 +83,15 @@ public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter{
         return new InMemoryUserDetailsManager(userDetails);\r
     }\r
 \r
-       @Override\r
-       protected void configure(HttpSecurity http) throws Exception {\r
-               http.authorizeRequests().anyRequest().fullyAuthenticated();\r
-           http.httpBasic().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);\r
+       @Bean\r
+       public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\r
+               http.authorizeHttpRequests().anyRequest().fullyAuthenticated();\r
+               http.httpBasic().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);\r
                http.csrf().disable();\r
+\r
+               return http.build();\r
        }\r
-       \r
+\r
     private static String[] decode(String encoded) {\r
         final byte[] decodedBytes \r
                 = Base64.getDecoder().decode(encoded.getBytes());\r
index 79c5818..2677f5b 100644 (file)
@@ -20,7 +20,7 @@ import java.util.ArrayList;
 import java.util.List;\r
 import java.util.Objects;\r
 \r
-import javax.validation.Valid;\r
+import jakarta.validation.Valid;\r
 \r
 import com.fasterxml.jackson.annotation.JsonProperty;\r
 \r
index 4d78f40..36d5d20 100644 (file)
@@ -15,7 +15,7 @@
  ******************************************************************************/\r
 package org.onap.ccsdk.apps.ms.vlantagapi.core.model;\r
 \r
-import javax.validation.Valid;\r
+import jakarta.validation.Valid;\r
 import java.util.Objects;\r
 import com.fasterxml.jackson.annotation.JsonProperty;\r
 \r
index e830d5c..0303627 100644 (file)
@@ -20,7 +20,7 @@ import java.util.ArrayList;
 import java.util.List;\r
 import java.util.Objects;\r
 \r
-import javax.validation.Valid;\r
+import jakarta.validation.Valid;\r
 \r
 import com.fasterxml.jackson.annotation.JsonProperty;\r
 \r
index 8613c70..f9f3baf 100644 (file)
@@ -20,7 +20,7 @@ import java.util.ArrayList;
 import java.util.List;\r
 import java.util.Objects;\r
 \r
-import javax.validation.Valid;\r
+import jakarta.validation.Valid;\r
 \r
 import com.fasterxml.jackson.annotation.JsonProperty;\r
 \r
index 4d2f1a5..260bb59 100644 (file)
@@ -20,7 +20,7 @@ import java.util.ArrayList;
 import java.util.List;\r
 import java.util.Objects;\r
 \r
-import javax.validation.Valid;\r
+import jakarta.validation.Valid;\r
 \r
 import com.fasterxml.jackson.annotation.JsonProperty;\r
 \r
index 1a207fd..3ec53ca 100644 (file)
@@ -17,7 +17,7 @@ package org.onap.ccsdk.apps.ms.vlantagapi.core.model;
 \r
 import java.util.Objects;\r
 \r
-import javax.validation.Valid;\r
+import jakarta.validation.Valid;\r
 \r
 import com.fasterxml.jackson.annotation.JsonProperty;\r
 \r
index 800346a..b1774ab 100644 (file)
@@ -15,7 +15,7 @@
  ******************************************************************************/\r
 package org.onap.ccsdk.apps.ms.vlantagapi.core.service;\r
 \r
-import javax.validation.Valid;\r
+import jakarta.validation.Valid;\r
 import javax.ws.rs.Consumes;\r
 import javax.ws.rs.GET;\r
 import javax.ws.rs.POST;\r
diff --git a/pom.xml b/pom.xml
index f206ca7..be50162 100755 (executable)
--- a/pom.xml
+++ b/pom.xml
@@ -4,8 +4,8 @@
 
     <parent>
         <groupId>org.onap.ccsdk.parent</groupId>
-        <artifactId>spring-boot-26-starter-parent</artifactId>
-        <version>2.5.4</version>
+        <artifactId>spring-boot-30-starter-parent</artifactId>
+        <version>2.6.0</version>
         <relativePath/>
     </parent>
 
@@ -22,6 +22,7 @@
     </organization>
 
     <modules>
+        <module>cadi</module>
         <module>services</module>
         <module>ms</module>
     </modules>
         <sonar.coverage.jacoco.xmlReportPaths>${project.reporting.outputDirectory}/jacoco-ut/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>
         <jacoco.version>0.8.8</jacoco.version>
         <gson.version>2.9.0</gson.version>
+           <springfox.version>3.0.0</springfox.version>
+        <aaf.cadi.version>2.1.21</aaf.cadi.version>
+        <ccsdk.sli.core.version>1.7.0-SNAPSHOT</ccsdk.sli.core.version>
+        <maven.compiler.source>1.11</maven.compiler.source>
+        <maven.compiler.target>1.11</maven.compiler.target>
     </properties>
 
     <build>
index 10ccb51..9665b22 100644 (file)
@@ -16,9 +16,9 @@
     <name>ccsdk-apps :: services :: ${project.artifactId}</name>
 
     <properties>
-        <shiro.version>1.5.0</shiro.version>
-        <aaf-shiro-bundle.version>2.1.13</aaf-shiro-bundle.version>
         <ccsdk.project.version>${project.version}</ccsdk.project.version>
+        <maven.compiler.source>1.11</maven.compiler.source>
+        <maven.compiler.target>1.11</maven.compiler.target>
         <ccsdk.build.timestamp>${maven.build.timestamp}</ccsdk.build.timestamp>
         <maven.build.timestamp.format>yyyyMMdd'T'HHmmss'Z'</maven.build.timestamp.format>
         <logging.analytics.version>1.6.9</logging.analytics.version>
             <groupId>io.swagger</groupId>
             <artifactId>swagger-annotations</artifactId>
         </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-logging</artifactId>
+                </exclusion>
+            </exclusions>
+               </dependency>
+
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-log4j2</artifactId>
         </dependency>
         <dependency>
             <groupId>io.springfox</groupId>
-            <artifactId>springfox-boot-starter</artifactId>
+            <artifactId>springfox-swagger2</artifactId>
             <version>${springfox.version}</version>
         </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+            <version>${springfox.version}</version>
+        </dependency>
+
         <dependency>
             <groupId>org.onap.ccsdk.sli.core</groupId>
             <artifactId>dblib-provider</artifactId>
index f2f638f..a700b15 100644 (file)
@@ -7,13 +7,14 @@ import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequestWrapper;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -145,6 +146,15 @@ public class ContentTypeFilter implements Filter {
         }
         
     }
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+    }
+
+    @Override
+    public void destroy() {
+
+    }
     
     
 }
index dd591fb..892cbe6 100644 (file)
@@ -16,20 +16,20 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ReadListener;
-import javax.servlet.ServletException;
-import javax.servlet.ServletInputStream;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.WriteListener;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpServletResponseWrapper;
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ReadListener;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletInputStream;
+import jakarta.servlet.ServletOutputStream;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.WriteListener;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequestWrapper;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponseWrapper;
 
 import org.onap.logging.filter.base.AbstractServletFilter;
 import org.slf4j.Logger;
@@ -271,9 +271,8 @@ public class PayloadLoggingFilter extends AbstractServletFilter implements Filte
 
                @SuppressWarnings("deprecation")
                @Override
-               public void setStatus(int sc, String sm) {
-                       super.setStatus(sc, sm);
-                       statusMessage = sm;
+               public void setStatus(int sc) {
+                       super.setStatus(sc);
                }
 
                public Response getMessageResponse() {
index c517f40..8912368 100644 (file)
@@ -7,6 +7,7 @@ import org.springframework.core.Ordered;
 import org.springframework.core.annotation.Order;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpStatus;
+import org.springframework.http.HttpStatusCode;
 import org.springframework.http.ResponseEntity;
 import org.springframework.http.converter.HttpMessageNotReadableException;
 import org.springframework.http.converter.HttpMessageNotWritableException;
@@ -29,89 +30,90 @@ import org.springframework.web.servlet.NoHandlerFoundException;
 import org.springframework.web.servlet.config.annotation.EnableWebMvc;
 import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
 
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
 
 @Order(Ordered.HIGHEST_PRECEDENCE)
 @RestControllerAdvice
 public class RestExceptionHandler extends ResponseEntityExceptionHandler {
 
+
     @Override
-    protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+    protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
       return createResponseEntity(new RestProtocolError("bad-method", "Method not supported", ex), status);
     }
 
     @Override
-    protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+    protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
         return createResponseEntity(new RestProtocolError("bad-media-type", "Media type not supported", ex), status);
     }
 
     @Override
-    protected ResponseEntity<Object> handleHttpMediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+    protected ResponseEntity<Object> handleHttpMediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
         return createResponseEntity(new RestProtocolError("bad-media-type", "Media type not acceptable", ex), status);
     }
 
     @Override
-    protected ResponseEntity<Object> handleMissingPathVariable(MissingPathVariableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+    protected ResponseEntity<Object> handleMissingPathVariable(MissingPathVariableException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
         return createResponseEntity(new RestApplicationError("missing-path", "Missing path variable", ex), status);
     }
 
     @Override
-    protected ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+    protected ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
         return createResponseEntity(new RestProtocolError("missing-param", "Missing servlet request parameter", ex), status);
     }
 
     @Override
-    protected ResponseEntity<Object> handleServletRequestBindingException(ServletRequestBindingException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+    protected ResponseEntity<Object> handleServletRequestBindingException(ServletRequestBindingException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
         return createResponseEntity(new RestApplicationError("request-binding", "Servlet binding exception", ex), status);
     }
 
     @Override
-    protected ResponseEntity<Object> handleConversionNotSupported(ConversionNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+    protected ResponseEntity<Object> handleConversionNotSupported(ConversionNotSupportedException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
         return createResponseEntity(new RestApplicationError("conversion", "Conversion not supported", ex), status);
     }
 
     @Override
-    protected ResponseEntity<Object> handleTypeMismatch(TypeMismatchException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+    protected ResponseEntity<Object> handleTypeMismatch(TypeMismatchException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
         return createResponseEntity(new RestProtocolError("type-mismatch", "Type mismatch", ex), status);
     }
 
     @Override
-    protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+    protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
         return createResponseEntity(new RestProtocolError("bad-message", "HTTP message not readable", ex), status);
     }
 
     @Override
-    protected ResponseEntity<Object> handleHttpMessageNotWritable(HttpMessageNotWritableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+    protected ResponseEntity<Object> handleHttpMessageNotWritable(HttpMessageNotWritableException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
         return createResponseEntity(new RestProtocolError("bad-message", "HTTP message not writable", ex), status);
     }
 
     @Override
-    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
         return createResponseEntity(new RestProtocolError("bad-message", "Method argument not valid", ex), status);
     }
 
     @Override
-    protected ResponseEntity<Object> handleMissingServletRequestPart(MissingServletRequestPartException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+    protected ResponseEntity<Object> handleMissingServletRequestPart(MissingServletRequestPartException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
         return createResponseEntity(new RestProtocolError("bad-message", "Missing servlet request part", ex), status);
     }
 
     @Override
-    protected ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+    protected ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
         return createResponseEntity(new RestApplicationError("binding-error", "Missing servlet request part", ex), status);
     }
 
     @Override
-    protected ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+    protected ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
         return createResponseEntity(new RestApplicationError("binding-error", "No handler found", ex), status);
     }
 
     @Override
-    protected ResponseEntity<Object> handleAsyncRequestTimeoutException(AsyncRequestTimeoutException ex, HttpHeaders headers, HttpStatus status, WebRequest webRequest) {
+    protected ResponseEntity<Object> handleAsyncRequestTimeoutException(AsyncRequestTimeoutException ex, HttpHeaders headers, HttpStatusCode status, WebRequest webRequest) {
         return createResponseEntity(new RestApplicationError("timeout", "Async request timeout", ex), status);
     }
 
     @Override
-    protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
+    protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
         return createResponseEntity(new RestApplicationError("internal-error", "Internal error", ex), status);
     }
 
@@ -121,10 +123,10 @@ public class RestExceptionHandler extends ResponseEntityExceptionHandler {
         if (request != null) {
             error.setErrorPath(request.getServletPath());
         }
-        return createResponseEntity(error,HttpStatus.valueOf(ex.getStatus()));
+        return createResponseEntity(error,HttpStatusCode.valueOf(ex.getStatus()));
     }
 
-    private ResponseEntity<Object> createResponseEntity(RestError restError, HttpStatus status) {
+    private ResponseEntity<Object> createResponseEntity(RestError restError, HttpStatusCode status) {
 
         return new ResponseEntity<>(new RestErrors(restError), status);
     }
index 242a601..b733af9 100644 (file)
@@ -11,6 +11,7 @@ import org.springframework.beans.TypeMismatchException;
 import org.springframework.core.MethodParameter;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpStatus;
+import org.springframework.http.HttpStatusCode;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.http.converter.HttpMessageNotReadableException;
@@ -39,82 +40,82 @@ public class RestExceptionHandlerTest {
     private static final Logger log = LoggerFactory.getLogger("RestExceptionHandler");
     private class RestExceptionHandlerWrapper extends RestExceptionHandler {
         @Override
-        public  ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+        public  ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
             return super.handleHttpRequestMethodNotSupported(ex, headers, status, request);
         }
 
         @Override
-        public ResponseEntity<Object> handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+        public ResponseEntity<Object> handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
             return super.handleHttpMediaTypeNotSupported(ex, headers, status, request);
         }
 
         @Override
-        public ResponseEntity<Object> handleHttpMediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+        public ResponseEntity<Object> handleHttpMediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
             return super.handleHttpMediaTypeNotAcceptable(ex, headers, status, request);
         }
 
         @Override
-        protected ResponseEntity<Object> handleMissingPathVariable(MissingPathVariableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+        protected ResponseEntity<Object> handleMissingPathVariable(MissingPathVariableException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
             return super.handleMissingPathVariable(ex, headers, status, request);
         }
 
         @Override
-        public ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+        public ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
             return super.handleMissingServletRequestParameter(ex, headers, status, request);
         }
 
         @Override
-        public ResponseEntity<Object> handleServletRequestBindingException(ServletRequestBindingException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+        public ResponseEntity<Object> handleServletRequestBindingException(ServletRequestBindingException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
             return super.handleServletRequestBindingException(ex, headers, status, request);
         }
 
         @Override
-        public ResponseEntity<Object> handleConversionNotSupported(ConversionNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+        public ResponseEntity<Object> handleConversionNotSupported(ConversionNotSupportedException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
             return super.handleConversionNotSupported(ex, headers, status, request);
         }
 
         @Override
-        public ResponseEntity<Object> handleTypeMismatch(TypeMismatchException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+        public ResponseEntity<Object> handleTypeMismatch(TypeMismatchException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
             return super.handleTypeMismatch(ex, headers, status, request);
         }
 
         @Override
-        public ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+        public ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
             return super.handleHttpMessageNotReadable(ex, headers, status, request);
         }
 
         @Override
-        public ResponseEntity<Object> handleHttpMessageNotWritable(HttpMessageNotWritableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+        public ResponseEntity<Object> handleHttpMessageNotWritable(HttpMessageNotWritableException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
             return super.handleHttpMessageNotWritable(ex, headers, status, request);
         }
 
         @Override
-        public ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+        public ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
             return super.handleMethodArgumentNotValid(ex, headers, status, request);
         }
 
         @Override
-        public ResponseEntity<Object> handleMissingServletRequestPart(MissingServletRequestPartException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+        public ResponseEntity<Object> handleMissingServletRequestPart(MissingServletRequestPartException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
             return super.handleMissingServletRequestPart(ex, headers, status, request);
         }
 
         @Override
-        public ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+        public ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
             return super.handleBindException(ex, headers, status, request);
         }
 
         @Override
-        public ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
+        public ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
             return super.handleNoHandlerFoundException(ex, headers, status, request);
         }
 
         @Override
-        public ResponseEntity<Object> handleAsyncRequestTimeoutException(AsyncRequestTimeoutException ex, HttpHeaders headers, HttpStatus status, WebRequest webRequest) {
+        public ResponseEntity<Object> handleAsyncRequestTimeoutException(AsyncRequestTimeoutException ex, HttpHeaders headers, HttpStatusCode status, WebRequest webRequest) {
             return super.handleAsyncRequestTimeoutException(ex, headers, status, webRequest);
         }
 
         @Override
-        public ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
+        public ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
             return super.handleExceptionInternal(ex, body, headers, status, request);
         }
     }